Moved gnocchi tempest tests under telemetry tempest plugin

* In order to achieve tempest plugin split goal, we have merged
  aodh, panko, ceilometer and gnocchi tempest plugin into
  telemetry tempest plugin

* gnocchi/tempest -> telemetry_tempest_plugin/gnocchi
* gnocchi/tests/functional_live -> telemetry_tempest_plugin/gnocchi

Change-Id: Icf6789afc143db3206af409c62a36e56edefd142
diff --git a/telemetry_tempest_plugin/config.py b/telemetry_tempest_plugin/config.py
index 87b50af..7fc17a8 100644
--- a/telemetry_tempest_plugin/config.py
+++ b/telemetry_tempest_plugin/config.py
@@ -27,6 +27,10 @@
                   cfg.BoolOpt("aodh_plugin",
                               default=True,
                               help="Whether or not Aodh is expected to be"
+                                   "available"),
+                  cfg.BoolOpt('gnocchi',
+                              default=True,
+                              help="Whether or not Gnocchi is expected to be"
                                    "available")]
 
 telemetry_group = cfg.OptGroup(name='telemetry',
@@ -38,6 +42,9 @@
 alarming_group = cfg.OptGroup(name='alarming_plugin',
                               title='Alarming Service Options')
 
+metric_group = cfg.OptGroup(name='metric',
+                            title='Metric Service Options')
+
 TelemetryGroup = [
     cfg.IntOpt('notification_wait',
                default=120,
@@ -75,3 +82,14 @@
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the alarming service."),
 ]
+
+metric_opts = [
+    cfg.StrOpt('catalog_type',
+               default='metric',
+               help="Catalog type of the Metric service."),
+    cfg.StrOpt('endpoint_type',
+               default='publicURL',
+               choices=['public', 'admin', 'internal',
+                        'publicURL', 'adminURL', 'internalURL'],
+               help="The endpoint type to use for the metric service."),
+]
diff --git a/telemetry_tempest_plugin/gnocchi/__init_.py b/telemetry_tempest_plugin/gnocchi/__init_.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/telemetry_tempest_plugin/gnocchi/__init_.py
diff --git a/telemetry_tempest_plugin/gnocchi/functional_live/__init__.py b/telemetry_tempest_plugin/gnocchi/functional_live/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/telemetry_tempest_plugin/gnocchi/functional_live/__init__.py
diff --git a/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/live.yaml b/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/live.yaml
new file mode 100644
index 0000000..d63cb09
--- /dev/null
+++ b/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/live.yaml
@@ -0,0 +1,739 @@
+#
+# Confirmation tests to run against a live web server.
+#
+# These act as a very basic sanity check.
+
+defaults:
+    request_headers:
+        x-auth-token: $ENVIRON['GNOCCHI_SERVICE_TOKEN']
+        authorization: $ENVIRON['GNOCCHI_AUTHORIZATION']
+
+tests:
+    - name: check /
+      GET: /
+
+    # Fail to create archive policy
+    - name: wrong archive policy content type
+      desc: attempt to create archive policy with invalid content-type
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: text/plain
+      status: 415
+      response_strings:
+          - Unsupported Media Type
+
+    - name: wrong method
+      desc: attempt to create archive policy with 'PUT' method
+      PUT: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      status: 405
+
+    - name: invalid authZ
+      desc: x-auth-token is invalid
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+          x-auth-token: 'hello'
+          authorization: 'basic hello:'
+      data:
+          name: medium
+          definition:
+              - granularity: 1 second
+      status: 401
+
+    - name: bad archive policy body
+      desc: archive policy contains invalid key 'cowsay'
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          cowsay: moo
+      status: 400
+      response_strings:
+          - "Invalid input: extra keys not allowed"
+
+    - name: missing definition
+      desc: archive policy is missing 'definition' keyword
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: medium
+      status: 400
+      response_strings:
+          - "Invalid input: required key not provided"
+
+    - name: empty definition
+      desc: empty definition for archive policy
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: medium
+          definition: []
+      status: 400
+      response_strings:
+          - "Invalid input: length of value must be at least 1"
+
+    - name: wrong value definition
+      desc: invalid type of 'definition' key
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: somename
+          definition: foobar
+      status: 400
+      response_strings:
+          - "Invalid input: expected a list"
+
+    - name: useless definition
+      desc: invalid archive policy definition
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: medium
+          definition:
+              - cowsay: moo
+      status: 400
+      response_strings:
+          - "Invalid input: extra keys not allowed"
+
+    #
+    # Create archive policy
+    #
+
+    - name: create archive policy
+      desc: create archve policy 'gabbilive' for live tests
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: gabbilive
+          back_window: 0
+          definition:
+              - granularity: 1 second
+                points: 60
+              - granularity: 2 second
+                timespan: 1 minute
+              - points: 5
+                timespan: 5 minute
+          aggregation_methods:
+              - mean
+              - min
+              - max
+      response_headers:
+          location: $SCHEME://$NETLOC/v1/archive_policy/gabbilive
+      status: 201
+
+    # Retrieve it correctly and then poorly
+
+    - name: get archive policy
+      desc: retrieve archive policy 'gabbilive' and asster its values
+      GET: $LOCATION
+      response_headers:
+          content-type: /application/json/
+      response_json_paths:
+          $.name: gabbilive
+          $.back_window: 0
+          $.definition[0].granularity: "0:00:01"
+          $.definition[0].points: 60
+          $.definition[0].timespan: "0:01:00"
+          $.definition[1].granularity: "0:00:02"
+          $.definition[1].points: 30
+          $.definition[1].timespan: "0:01:00"
+          $.definition[2].granularity: "0:01:00"
+          $.definition[2].points: 5
+          $.definition[2].timespan: "0:05:00"
+      response_json_paths:
+          $.aggregation_methods.`sorted`: ["max", "mean", "min"]
+
+    - name: get wrong accept
+      desc: invalid 'accept' header
+      GET: /v1/archive_policy/medium
+      request_headers:
+          accept: text/plain
+      status: 406
+
+    # Unexpected methods
+
+    - name: post single archive
+      desc: unexpected 'POST' request to archive policy
+      POST: /v1/archive_policy/gabbilive
+      status: 405
+
+    - name: put single archive
+      desc: unexpected 'PUT' request to archive policy
+      PUT: /v1/archive_policy/gabbilive
+      status: 405
+
+    # Duplicated archive policy names ain't allowed
+
+    - name: create duplicate archive policy
+      desc: create archve policy 'gabbilive' for live tests
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: gabbilive
+          definition:
+              - granularity: 30 second
+                points: 60
+      status: 409
+      response_strings:
+          - Archive policy gabbilive already exists
+
+    # Create a unicode named policy
+
+    - name: post unicode policy name
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: ✔éñ☃
+          definition:
+              - granularity: 1 minute
+                points: 20
+      status: 201
+      response_headers:
+          location: $SCHEME://$NETLOC/v1/archive_policy/%E2%9C%94%C3%A9%C3%B1%E2%98%83
+      response_json_paths:
+          name: ✔éñ☃
+
+    - name: retrieve unicode policy name
+      GET: $LOCATION
+      response_json_paths:
+          name: ✔éñ☃
+
+    - name: delete unicode archive policy
+      DELETE: /v1/archive_policy/%E2%9C%94%C3%A9%C3%B1%E2%98%83
+      status: 204
+
+    # It really is gone
+
+    - name: confirm delete
+      desc: assert deleted unicode policy is not available
+      GET: /v1/archive_policy/%E2%9C%94%C3%A9%C3%B1%E2%98%83
+      status: 404
+
+    # Fail to delete one that does not exist
+
+    - name: delete missing archive
+      desc: delete non-existent archive policy
+      DELETE: /v1/archive_policy/grandiose
+      status: 404
+      response_strings:
+          - Archive policy grandiose does not exist
+
+    # Attempt to create illogical policies
+
+    - name: create illogical policy
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: complex
+          definition:
+              - granularity: 1 second
+                points: 60
+                timespan: "0:01:01"
+      status: 400
+      response_strings:
+          - timespan ≠ granularity × points
+
+    - name: create identical granularities policy
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: complex
+          definition:
+              - granularity: 1 second
+                points: 60
+              - granularity: 1 second
+                points: 120
+      status: 400
+      response_strings:
+          - "More than one archive policy uses granularity `1.0'"
+
+    - name: policy invalid unit
+      desc: invalid unit for archive policy 'timespan' key
+      POST: /v1/archive_policy
+      request_headers:
+          content-type: application/json
+      data:
+          name: 227d0e1f-4295-4e4b-8515-c296c47d71d3
+          definition:
+              - granularity: 1 second
+                timespan: "1 shenanigan"
+      status: 400
+
+    #
+    # Archive policy rules
+    #
+
+    - name: create archive policy rule1
+      POST: /v1/archive_policy_rule
+      request_headers:
+          content-type: application/json
+      data:
+          name: gabbilive_rule
+          metric_pattern: "live.*"
+          archive_policy_name: gabbilive
+      status: 201
+      response_json_paths:
+        $.metric_pattern: "live.*"
+        $.archive_policy_name: gabbilive
+        $.name: gabbilive_rule
+
+    - name: create invalid archive policy rule
+      POST: /v1/archive_policy_rule
+      request_headers:
+          content-type: application/json
+      data:
+        name: test_rule
+        metric_pattern: "disk.foo.*"
+      status: 400
+
+    - name: missing auth archive policy rule
+      POST: /v1/archive_policy_rule
+      request_headers:
+          content-type: application/json
+          x-auth-token: 'hello'
+          authorization: 'basic hello:'
+      data:
+        name: test_rule
+        metric_pattern: "disk.foo.*"
+        archive_policy_name: low
+      status: 401
+
+    - name: wrong archive policy rule content type
+      POST: /v1/archive_policy_rule
+      request_headers:
+          content-type: text/plain
+      status: 415
+      response_strings:
+          - Unsupported Media Type
+
+    - name: bad archive policy rule body
+      POST: /v1/archive_policy_rule
+      request_headers:
+          content-type: application/json
+      data:
+          whaa: foobar
+      status: 400
+      response_strings:
+          - "Invalid input: extra keys not allowed"
+
+    # get an archive policy rules
+
+    - name: get all archive policy rules
+      GET: /v1/archive_policy_rule
+      status: 200
+      response_json_paths:
+          $[\name][0].name: "gabbilive_rule"
+          $[\name][0].metric_pattern: "live.*"
+          $[\name][0].archive_policy_name: "gabbilive"
+
+    - name: get unknown archive policy rule
+      GET: /v1/archive_policy_rule/foo
+      status: 404
+
+
+    - name: get archive policy rule
+      GET: /v1/archive_policy_rule/gabbilive_rule
+      status: 200
+      response_json_paths:
+          $.metric_pattern: "live.*"
+          $.archive_policy_name: "gabbilive"
+          $.name: "gabbilive_rule"
+
+    - name: delete archive policy in use
+      desc: fails due to https://bugs.launchpad.net/gnocchi/+bug/1569781
+      DELETE: /v1/archive_policy/gabbilive
+      status: 400
+
+    #
+    # Metrics
+    #
+
+
+    - name: get all metrics
+      GET: /v1/metric
+      status: 200
+
+    - name: create metric with name and rule
+      POST: /v1/metric
+      request_headers:
+          content-type: application/json
+      data:
+          name: "live.io.rate"
+      status: 201
+      response_json_paths:
+          $.archive_policy_name: gabbilive
+          $.name: live.io.rate
+
+    - name: assert metric is present in listing
+      GET: /v1/metric?id=$HISTORY['create metric with name and rule'].$RESPONSE['$.id']
+      response_json_paths:
+          $.`len`: 1
+
+    - name: assert metric is the only one with this policy
+      GET: /v1/metric?archive_policy_name=gabbilive
+      response_json_paths:
+          $.`len`: 1
+
+    - name: delete metric
+      DELETE: /v1/metric/$HISTORY['create metric with name and rule'].$RESPONSE['$.id']
+      status: 204
+
+    - name: assert metric is expunged
+      GET: $HISTORY['assert metric is present in listing'].$URL&status=delete
+      poll:
+          count: 360
+          delay: 1
+      response_json_paths:
+          $.`len`: 0
+
+    - name: create metric with name and policy
+      POST: /v1/metric
+      request_headers:
+          content-type: application/json
+      data:
+          name: "aagabbi.live.metric"
+          archive_policy_name: "gabbilive"
+      status: 201
+      response_json_paths:
+          $.archive_policy_name: gabbilive
+          $.name: "aagabbi.live.metric"
+
+    - name: get valid metric id
+      GET: $LOCATION
+      status: 200
+      response_json_paths:
+        $.archive_policy.name: gabbilive
+
+    - name: delete the metric
+      DELETE: /v1/metric/$RESPONSE['$.id']
+      status: 204
+
+    - name: ensure the metric is delete
+      GET: /v1/metric/$HISTORY['get valid metric id'].$RESPONSE['$.id']
+      status: 404
+
+    - name: create metric bad archive policy
+      POST: /v1/metric
+      request_headers:
+          content-type: application/json
+      data:
+          archive_policy_name: 2e2675aa-105e-4664-a30d-c407e6a0ea7f
+      status: 400
+      response_strings:
+          - Archive policy 2e2675aa-105e-4664-a30d-c407e6a0ea7f does not exist
+
+    - name: create metric bad content-type
+      POST: /v1/metric
+      request_headers:
+          content-type: plain/text
+      data: '{"archive_policy_name": "cookies"}'
+      status: 415
+
+
+    #
+    # Cleanup
+    #
+
+    - name: delete archive policy rule
+      DELETE: /v1/archive_policy_rule/gabbilive_rule
+      status: 204
+
+    - name: confirm delete archive policy rule
+      DELETE: /v1/archive_policy_rule/gabbilive_rule
+      status: 404
+
+
+    #
+    # Resources section
+    #
+
+    - name: root of resource
+      GET: /v1/resource
+      response_json_paths:
+          $.generic: $SCHEME://$NETLOC/v1/resource/generic
+
+    - name: typo of resource
+      GET: /v1/resoue
+      status: 404
+
+    - name: typo of resource extra
+      GET: /v1/resource/foobar
+      status: 404
+
+    - name: generic resource
+      GET: /v1/resource/generic
+      status: 200
+
+    - name: post resource type
+      POST: /v1/resource_type
+      request_headers:
+          content-type: application/json
+      data:
+          name: myresource
+          attributes:
+              display_name:
+                  type: string
+                  required: true
+                  max_length: 5
+                  min_length: 2
+      status: 201
+      response_headers:
+          location: $SCHEME://$NETLOC/v1/resource_type/myresource
+
+    - name: add an attribute
+      PATCH: /v1/resource_type/myresource
+      request_headers:
+          content-type: application/json-patch+json
+      data:
+        - op: "add"
+          path: "/attributes/awesome-stuff"
+          value: {"type": "bool", "required": false}
+      status: 200
+      response_json_paths:
+          $.name: myresource
+          $.attributes."awesome-stuff".type: bool
+          $.attributes.[*].`len`: 2
+
+    - name: remove an attribute
+      PATCH: /v1/resource_type/myresource
+      request_headers:
+          content-type: application/json-patch+json
+      data:
+        - op: "remove"
+          path: "/attributes/awesome-stuff"
+      status: 200
+      response_json_paths:
+          $.name: myresource
+          $.attributes.display_name.type: string
+          $.attributes.[*].`len`: 1
+
+    - name: myresource resource bad accept
+      desc: Expect 406 on bad accept type
+      request_headers:
+          accept: text/plain
+      GET: /v1/resource/myresource
+      status: 406
+      response_strings:
+          - 406 Not Acceptable
+
+    - name: myresource resource complex accept
+      desc: failover accept media type appropriately
+      request_headers:
+          accept: text/plain, application/json; q=0.8
+      GET: /v1/resource/myresource
+      status: 200
+
+    - name: post myresource resource
+      POST: /v1/resource/myresource
+      request_headers:
+          content-type: application/json
+      data:
+          id: 2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+          user_id: 126204ef-989a-46fd-999b-ee45c8108f31
+          project_id: 98e785d7-9487-4159-8ab8-8230ec37537a
+          display_name: myvm
+          metrics:
+              vcpus:
+                  archive_policy_name: gabbilive
+      status: 201
+      response_json_paths:
+          $.id: 2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+          $.user_id: 126204ef-989a-46fd-999b-ee45c8108f31
+          $.project_id: 98e785d7-9487-4159-8ab8-8230ec37537a
+          $.display_name: "myvm"
+
+    - name: get myresource resource
+      GET: $LOCATION
+      status: 200
+      response_json_paths:
+          $.id: 2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+          $.user_id: 126204ef-989a-46fd-999b-ee45c8108f31
+          $.project_id: 98e785d7-9487-4159-8ab8-8230ec37537a
+          $.display_name: "myvm"
+
+    - name: get vcpus metric
+      GET: /v1/metric/$HISTORY['get myresource resource'].$RESPONSE['$.metrics.vcpus']
+      status: 200
+      response_json_paths:
+          $.name: vcpus
+          $.resource.id: 2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+
+    - name: search for myresource resource via user_id
+      POST: /v1/search/resource/myresource
+      request_headers:
+        content-type: application/json
+      data:
+        =:
+          user_id: "126204ef-989a-46fd-999b-ee45c8108f31"
+      response_json_paths:
+        $..id: 2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+        $..user_id: 126204ef-989a-46fd-999b-ee45c8108f31
+        $..project_id: 98e785d7-9487-4159-8ab8-8230ec37537a
+        $..display_name: myvm
+
+    - name: search for myresource resource via user_id and 'generic' type
+      POST: /v1/search/resource/generic
+      request_headers:
+        content-type: application/json
+      data:
+        =:
+          id: "2ae35573-7f9f-4bb1-aae8-dad8dff5706e"
+      response_strings:
+          - '"user_id": "126204ef-989a-46fd-999b-ee45c8108f31"'
+
+    - name: search for myresource resource via user_id and project_id
+      POST: /v1/search/resource/generic
+      request_headers:
+        content-type: application/json
+      data:
+        and:
+          - =:
+              user_id: "126204ef-989a-46fd-999b-ee45c8108f31"
+          - =:
+              project_id: "98e785d7-9487-4159-8ab8-8230ec37537a"
+      response_strings:
+          - '"id": "2ae35573-7f9f-4bb1-aae8-dad8dff5706e"'
+
+    - name: patch myresource resource
+      PATCH: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+      request_headers:
+          content-type: application/json
+      data:
+          display_name: myvm2
+      status: 200
+      response_json_paths:
+          display_name: myvm2
+
+    - name: post some measures to the metric on myresource
+      POST: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e/metric/vcpus/measures
+      request_headers:
+          content-type: application/json
+      data:
+          - timestamp: "2015-03-06T14:33:57"
+            value: 2
+          - timestamp: "2015-03-06T14:34:12"
+            value: 2
+      status: 202
+
+    - name: get myresource measures with poll
+      GET: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e/metric/vcpus/measures
+      # wait up to 60 seconds before policy is deleted
+      poll:
+          count: 60
+          delay: 1
+      response_json_paths:
+          $[0][2]: 2
+          $[1][2]: 2
+
+    - name: post some more measures to the metric on myresource
+      POST: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e/metric/vcpus/measures
+      request_headers:
+          content-type: application/json
+      data:
+          - timestamp: "2015-03-06T14:34:15"
+            value: 5
+          - timestamp: "2015-03-06T14:34:20"
+            value: 5
+      status: 202
+
+    - name: get myresource measures with refresh
+      GET: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e/metric/vcpus/measures?refresh=true
+      response_json_paths:
+          $[0][2]: 2
+          $[1][2]: 4
+          $[2][2]: 2
+          $[3][2]: 2
+          $[4][2]: 5
+          $[5][2]: 5
+
+    #
+    # Search for resources
+    #
+
+    - name: typo of search
+      POST: /v1/search/notexists
+      status: 404
+
+    - name: typo of search in resource
+      POST: /v1/search/resource/foobar
+      status: 404
+
+    - name: search with invalid uuid
+      POST: /v1/search/resource/generic
+      request_headers:
+        content-type: application/json
+      data:
+        =:
+          id: "cd9eef"
+      status: 200
+      response_json_paths:
+          $.`len`: 0
+
+    - name: assert vcpus metric exists in listing
+      GET: /v1/metric?id=$HISTORY['get myresource resource'].$RESPONSE['$.metrics.vcpus']
+      poll:
+          count: 360
+          delay: 1
+      response_json_paths:
+          $.`len`: 1
+
+    - name: delete myresource resource
+      DELETE: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+      status: 204
+
+      # assert resource is really deleted
+    - name: assert resource resource is deleted
+      GET: /v1/resource/myresource/2ae35573-7f9f-4bb1-aae8-dad8dff5706e
+      status: 404
+
+    - name: assert vcpus metric is really expurged
+      GET: $HISTORY['assert vcpus metric exists in listing'].$URL&status=delete
+      poll:
+          count: 360
+          delay: 1
+      response_json_paths:
+          $.`len`: 0
+
+    - name: post myresource resource no data
+      POST: /v1/resource/myresource
+      request_headers:
+          content-type: application/json
+      status: 400
+
+    - name: assert no metrics have the gabbilive policy
+      GET: $HISTORY['assert metric is the only one with this policy'].$URL
+      response_json_paths:
+          $.`len`: 0
+
+    - name: assert no delete metrics have the gabbilive policy
+      GET: $HISTORY['assert metric is the only one with this policy'].$URL&status=delete
+      response_json_paths:
+          $.`len`: 0
+
+    - name: delete single archive policy cleanup
+      DELETE: /v1/archive_policy/gabbilive
+      poll:
+          count: 360
+          delay: 1
+      status: 204
+
+    # It really is gone
+
+    - name: delete our resource type
+      DELETE: /v1/resource_type/myresource
+      status: 204
+
+    - name: confirm delete of cleanup
+      GET: /v1/archive_policy/gabbilive
+      status: 404
diff --git a/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/search-resource.yaml b/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/search-resource.yaml
new file mode 100644
index 0000000..fe25478
--- /dev/null
+++ b/telemetry_tempest_plugin/gnocchi/functional_live/gabbits/search-resource.yaml
@@ -0,0 +1,275 @@
+#
+# Tests to confirm resources are searchable. Run against a live setup.
+# URL: http://gnocchi.xyz/rest.html#searching-for-resources
+#
+# Instance-ResourceID-1: a64ca14f-bc7c-45b0-aa85-42cd2179e1e2
+# Instance-ResourceID-2: 7ccccfa0-92ce-4225-80ca-3ac9cb122d6a
+# Instance-ResourceID-3: c442a47c-eb33-46ce-9665-f3aa0bef54e7
+#
+# UserID-1: 33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07
+# UserID-2: 81d82ef3-4deb-499d-9270-9aeb5a3ec5fe
+#
+# ProjectID-1: c9a5f184-c0d0-4daa-83c3-af6fdc0879e6
+# ProjectID-2: 40eba01c-b348-49b8-803f-67123251a00a
+#
+# ImageID-1: 7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d
+# ImageID-2: b01f2588-89dc-46b2-897b-fffae1e10975
+#
+
+defaults:
+    request_headers:
+        x-auth-token: $ENVIRON['GNOCCHI_SERVICE_TOKEN']
+        authorization: $ENVIRON['GNOCCHI_AUTHORIZATION']
+
+tests:
+    #
+    # Setup resource types if don't exist
+    #
+
+    - name: create new resource type 'instance-like'
+      POST: /v1/resource_type
+      status: 201
+      request_headers:
+          content-type: application/json
+      data:
+          name: instance-like
+          attributes:
+              display_name:
+                  type: string
+                  required: True
+              flavor_id:
+                  type: string
+                  required: True
+              host:
+                  type: string
+                  required: True
+              image_ref:
+                  type: string
+                  required: False
+              server_group:
+                  type: string
+                  required: False
+
+    - name: create new resource type 'image-like'
+      POST: /v1/resource_type
+      status: 201
+      request_headers:
+          content-type: application/json
+      data:
+          name: image-like
+          attributes:
+              name:
+                  type: string
+                  required: True
+              disk_format:
+                  type: string
+                  required: True
+              container_format:
+                  type: string
+                  required: True
+
+    #
+    # Setup test resources
+    #
+    - name: helper. create instance-like resource-1
+      POST: /v1/resource/instance-like
+      request_headers:
+          content-type: application/json
+      data:
+          display_name: vm-gabbi-1
+          id: a64ca14f-bc7c-45b0-aa85-42cd2179e1e2
+          user_id: 33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07
+          flavor_id: "1"
+          image_ref: 7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d
+          host: compute-0-gabbi.localdomain
+          project_id: c9a5f184-c0d0-4daa-83c3-af6fdc0879e6
+      status: 201
+
+    - name: helper. create instance-like resource-2
+      POST: /v1/resource/instance-like
+      request_headers:
+          content-type: application/json
+      data:
+          display_name: vm-gabbi-2
+          id: 7ccccfa0-92ce-4225-80ca-3ac9cb122d6a
+          user_id: 33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07
+          flavor_id: "2"
+          image_ref: b01f2588-89dc-46b2-897b-fffae1e10975
+          host: compute-1-gabbi.localdomain
+          project_id: c9a5f184-c0d0-4daa-83c3-af6fdc0879e6
+      status: 201
+
+    - name: helper. create instance-like resource-3
+      POST: /v1/resource/instance-like
+      request_headers:
+          content-type: application/json
+      data:
+          display_name: vm-gabbi-3
+          id: c442a47c-eb33-46ce-9665-f3aa0bef54e7
+          user_id: 81d82ef3-4deb-499d-9270-9aeb5a3ec5fe
+          flavor_id: "2"
+          image_ref: b01f2588-89dc-46b2-897b-fffae1e10975
+          host: compute-1-gabbi.localdomain
+          project_id: 40eba01c-b348-49b8-803f-67123251a00a
+      status: 201
+
+    - name: helper. create image-like resource-1
+      POST: /v1/resource/image-like
+      request_headers:
+          content-type: application/json
+      data:
+          id: 7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d
+          container_format: bare
+          disk_format: qcow2
+          name: gabbi-image-1
+          user_id: 81d82ef3-4deb-499d-9270-9aeb5a3ec5fe
+          project_id: 40eba01c-b348-49b8-803f-67123251a00a
+      status: 201
+
+    #
+    # Actual tests
+    #
+
+    - name: search for all resources with a specific user_id
+      desc: search through all resource types
+      POST: /v1/search/resource/generic
+      request_headers:
+          content-type: application/json
+      data:
+          =:
+              user_id: 81d82ef3-4deb-499d-9270-9aeb5a3ec5fe
+      status: 200
+      response_json_paths:
+          $.`len`: 2
+      response_json_paths:
+          $.[0].type: instance-like
+          $.[1].type: image-like
+          $.[0].id: c442a47c-eb33-46ce-9665-f3aa0bef54e7
+          $.[1].id: 7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d
+
+    - name: search for all resources of instance-like type create by specific user_id
+      desc: all instances created by a specified user
+      POST: /v1/search/resource/generic
+      request_headers:
+          content-type: application/json
+      data:
+          and:
+              - =:
+                  type: instance-like
+              - =:
+                  user_id: 33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07
+      status: 200
+      response_json_paths:
+          $.`len`: 2
+      response_strings:
+          - '"id": "a64ca14f-bc7c-45b0-aa85-42cd2179e1e2"'
+          - '"id": "7ccccfa0-92ce-4225-80ca-3ac9cb122d6a"'
+      response_json_paths:
+          $.[0].id: a64ca14f-bc7c-45b0-aa85-42cd2179e1e2
+          $.[1].id: 7ccccfa0-92ce-4225-80ca-3ac9cb122d6a
+          $.[0].type: instance-like
+          $.[1].type: instance-like
+          $.[0].metrics.`len`: 0
+          $.[1].metrics.`len`: 0
+
+    - name: search for all resources with a specific project_id
+      desc: search for all resources in a specific project
+      POST: /v1/search/resource/generic
+      request_headers:
+          content-type: application/json
+      data:
+          =:
+              project_id: c9a5f184-c0d0-4daa-83c3-af6fdc0879e6
+      status: 200
+      response_json_paths:
+          $.`len`: 2
+
+    - name: search for intances on a specific compute using "like" keyword
+      desc: search for vms hosted on a specific compute node
+      POST: /v1/search/resource/instance-like
+      request_headers:
+          content-type: application/json
+      data:
+          like:
+              host: 'compute-1-gabbi%'
+      response_json_paths:
+          $.`len`: 2
+      response_strings:
+          - '"project_id": "40eba01c-b348-49b8-803f-67123251a00a"'
+          - '"project_id": "c9a5f184-c0d0-4daa-83c3-af6fdc0879e6"'
+          - '"user_id": "33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07"'
+          - '"user_id": "81d82ef3-4deb-499d-9270-9aeb5a3ec5fe"'
+          - '"display_name": "vm-gabbi-2"'
+          - '"display_name": "vm-gabbi-3"'
+
+    - name: search for instances using complex search with "like" keyword and user_id
+      desc: search for vms of specified user hosted on a specific compute node
+      POST: /v1/search/resource/instance-like
+      request_headers:
+          content-type: application/json
+      data:
+          and:
+            - like:
+                  host: 'compute-%-gabbi%'
+            - =:
+                  user_id: 33ba83ca-2f12-4ad6-8fa2-bc8b55d36e07
+      response_json_paths:
+          $.`len`: 2
+      response_strings:
+          - '"display_name": "vm-gabbi-1"'
+          - '"display_name": "vm-gabbi-2"'
+          - '"project_id": "c9a5f184-c0d0-4daa-83c3-af6fdc0879e6"'
+
+    - name: search for resources of instance-like or image-like type with specific user_id
+      desc: search for all image-like or instance-like resources created by a specific user
+      POST: /v1/search/resource/generic
+      request_headers:
+          content-type: application/json
+      data:
+          and:
+              - =:
+                  user_id: 81d82ef3-4deb-499d-9270-9aeb5a3ec5fe
+
+              - or:
+                  - =:
+                      type: instance-like
+
+                  - =:
+                      type: image-like
+      status: 200
+      response_json_paths:
+          $.`len`: 2
+      response_strings:
+          - '"type": "image-like"'
+          - '"type": "instance-like"'
+          - '"id": "7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d"'
+          - '"id": "c442a47c-eb33-46ce-9665-f3aa0bef54e7"'
+
+    #
+    # Tear down resources
+    #
+
+    - name: helper. delete instance-like resource-1
+      DELETE: /v1/resource/instance-like/a64ca14f-bc7c-45b0-aa85-42cd2179e1e2
+      status: 204
+
+    - name: helper. delete instance-like resource-2
+      DELETE: /v1/resource/instance-like/7ccccfa0-92ce-4225-80ca-3ac9cb122d6a
+      status: 204
+
+    - name: helper. delete instance-like resource-3
+      DELETE: /v1/resource/instance-like/c442a47c-eb33-46ce-9665-f3aa0bef54e7
+      status: 204
+
+    - name: helper. delete image-like resource
+      DELETE: /v1/resource/image-like/7ab2f7ae-7af5-4469-bdc8-3c0f6dfab75d
+      status: 204
+
+    - name: helper. delete resource-type instance-like
+      DELETE: /v1/resource_type/instance-like
+      status: 204
+
+    - name: helper. delete resource-type image-like
+      DELETE: /v1/resource_type/image-like
+      status: 204
+
diff --git a/telemetry_tempest_plugin/gnocchi/scenario/__init__.py b/telemetry_tempest_plugin/gnocchi/scenario/__init__.py
new file mode 100644
index 0000000..3ad1239
--- /dev/null
+++ b/telemetry_tempest_plugin/gnocchi/scenario/__init__.py
@@ -0,0 +1,110 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from __future__ import absolute_import
+
+import os
+import unittest
+
+from gabbi import runner
+from gabbi import suitemaker
+from gabbi import utils
+import six.moves.urllib.parse as urlparse
+from tempest import config
+import tempest.test
+
+CONF = config.CONF
+
+TEST_DIR = os.path.join(os.path.dirname(__file__), '..',
+                        'gnocchi', 'functional_live', 'gabbits')
+
+
+class GnocchiGabbiTest(tempest.test.BaseTestCase):
+    credentials = ['admin']
+
+    TIMEOUT_SCALING_FACTOR = 5
+
+    @classmethod
+    def skip_checks(cls):
+        super(GnocchiGabbiTest, cls).skip_checks()
+        if not CONF.service_available.gnocchi:
+            raise cls.skipException("Gnocchi support is required")
+
+    def _do_test(self, filename):
+        token = self.os_admin.auth_provider.get_token()
+        url = self.os_admin.auth_provider.base_url(
+            {'service': CONF.metric.catalog_type,
+             'endpoint_type': CONF.metric.endpoint_type})
+
+        parsed_url = urlparse.urlsplit(url)
+        prefix = parsed_url.path.rstrip('/')  # turn it into a prefix
+        if parsed_url.scheme == 'https':
+            port = 443
+            require_ssl = True
+        else:
+            port = 80
+            require_ssl = False
+        host = parsed_url.hostname
+        if parsed_url.port:
+            port = parsed_url.port
+
+        os.environ["GNOCCHI_SERVICE_TOKEN"] = token
+        os.environ["GNOCCHI_AUTHORIZATION"] = "not used"
+
+        with open(os.path.join(TEST_DIR, filename)) as f:
+            suite_dict = utils.load_yaml(f)
+            suite_dict.setdefault('defaults', {})['ssl'] = require_ssl
+            test_suite = suitemaker.test_suite_from_dict(
+                loader=unittest.defaultTestLoader,
+                test_base_name="gabbi",
+                suite_dict=suite_dict,
+                test_directory=TEST_DIR,
+                host=host, port=port,
+                fixture_module=None,
+                intercept=None,
+                prefix=prefix,
+                handlers=runner.initialize_handlers([]),
+                test_loader_name="tempest")
+
+            # NOTE(sileht): We hide stdout/stderr and reraise the failure
+            # manually, tempest will print it itself.
+            with open(os.devnull, 'w') as stream:
+                result = unittest.TextTestRunner(
+                    stream=stream, verbosity=0, failfast=True,
+                ).run(test_suite)
+
+            if not result.wasSuccessful():
+                failures = (result.errors + result.failures +
+                            result.unexpectedSuccesses)
+                if failures:
+                    test, bt = failures[0]
+                    name = test.test_data.get('name', test.id())
+                    msg = 'From test "%s" :\n%s' % (name, bt)
+                    self.fail(msg)
+
+            self.assertTrue(result.wasSuccessful())
+
+
+def test_maker(name, filename):
+    def test(self):
+        self._do_test(filename)
+        test.__name__ = name
+    return test
+
+
+# Create one scenario per yaml file
+for filename in os.listdir(TEST_DIR):
+    if not filename.endswith('.yaml'):
+        continue
+    name = "test_%s" % filename[:-5].lower().replace("-", "_")
+    setattr(GnocchiGabbiTest, name,
+            test_maker(name, filename))
diff --git a/telemetry_tempest_plugin/plugin.py b/telemetry_tempest_plugin/plugin.py
index 0d8fdfd..2f2861e 100644
--- a/telemetry_tempest_plugin/plugin.py
+++ b/telemetry_tempest_plugin/plugin.py
@@ -43,6 +43,9 @@
         config.register_opt_group(
             conf, tempest_config.alarming_group,
             tempest_config.AlarmingGroup)
+        config.register_opt_group(
+            conf, tempest_config.metric_group,
+            tempest_config.metric_opts)
 
     def get_opt_lists(self):
         return [(tempest_config.telemetry_group.name,
@@ -52,4 +55,6 @@
                 (config.service_available_group.name,
                  tempest_config.service_option),
                 (tempest_config.alarming_group.name,
-                 tempest_config.AlarmingGroup)]
+                 tempest_config.AlarmingGroup),
+                (tempest_config.metric_group.name,
+                 tempest_config.metric_opts)]