diff --git a/doc/v3/api_samples/extension-info/extensions-get-resp.json b/doc/v3/api_samples/extension-info/extensions-get-resp.json
new file mode 100644
index 000000000000..1ee70325681b
--- /dev/null
+++ b/doc/v3/api_samples/extension-info/extensions-get-resp.json
@@ -0,0 +1,9 @@
+{
+ "extension": {
+ "alias": "flavors",
+ "description": "Flavors Extension.",
+ "name": "flavors",
+ "namespace": "http://docs.openstack.org/compute/core/flavors/v3",
+ "version": 1
+ }
+}
\ No newline at end of file
diff --git a/doc/v3/api_samples/extension-info/extensions-get-resp.xml b/doc/v3/api_samples/extension-info/extensions-get-resp.xml
new file mode 100644
index 000000000000..198b3a9d7836
--- /dev/null
+++ b/doc/v3/api_samples/extension-info/extensions-get-resp.xml
@@ -0,0 +1,4 @@
+
+
+ Flavors Extension.
+
\ No newline at end of file
diff --git a/doc/v3/api_samples/extension-info/extensions-list-resp.json b/doc/v3/api_samples/extension-info/extensions-list-resp.json
new file mode 100644
index 000000000000..db49736df0d9
--- /dev/null
+++ b/doc/v3/api_samples/extension-info/extensions-list-resp.json
@@ -0,0 +1,11 @@
+{
+ "extensions": [
+ {
+ "alias": "flavors",
+ "description": "Flavors Extension.",
+ "name": "flavors",
+ "namespace": "http://docs.openstack.org/compute/core/flavors/v3",
+ "version": 1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/doc/v3/api_samples/extension-info/extensions-list-resp.xml b/doc/v3/api_samples/extension-info/extensions-list-resp.xml
new file mode 100644
index 000000000000..68091f6c24eb
--- /dev/null
+++ b/doc/v3/api_samples/extension-info/extensions-list-resp.xml
@@ -0,0 +1,6 @@
+
+
+
+ Flavors Extension.
+
+
\ No newline at end of file
diff --git a/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.json b/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.json
new file mode 100644
index 000000000000..3e84af05ef93
--- /dev/null
+++ b/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.json
@@ -0,0 +1,27 @@
+{
+ "instance_action": {
+ "action": "reboot",
+ "events": [
+ {
+ "event": "schedule",
+ "finish_time": "2012-12-05T01:02:00.000000",
+ "result": "Success",
+ "start_time": "2012-12-05T01:00:02.000000",
+ "traceback": ""
+ },
+ {
+ "event": "compute_create",
+ "finish_time": "2012-12-05T01:04:00.000000",
+ "result": "Success",
+ "start_time": "2012-12-05T01:03:00.000000",
+ "traceback": ""
+ }
+ ],
+ "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13",
+ "message": "",
+ "project_id": "147",
+ "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8",
+ "start_time": "2012-12-05T00:00:00.000000",
+ "user_id": "789"
+ }
+}
\ No newline at end of file
diff --git a/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.xml b/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.xml
new file mode 100644
index 000000000000..122b0bac17b5
--- /dev/null
+++ b/doc/v3/api_samples/os-instance-actions/instance-action-get-resp.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.json b/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.json
new file mode 100644
index 000000000000..8884f06b4868
--- /dev/null
+++ b/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.json
@@ -0,0 +1,22 @@
+{
+ "instance_actions": [
+ {
+ "action": "resize",
+ "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13",
+ "message": "",
+ "project_id": "842",
+ "request_id": "req-25517360-b757-47d3-be45-0e8d2a01b36a",
+ "start_time": "2012-12-05T01:00:00.000000",
+ "user_id": "789"
+ },
+ {
+ "action": "reboot",
+ "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13",
+ "message": "",
+ "project_id": "147",
+ "request_id": "req-3293a3f1-b44c-4609-b8d2-d81b105636b8",
+ "start_time": "2012-12-05T00:00:00.000000",
+ "user_id": "789"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml b/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml
new file mode 100644
index 000000000000..e76193f5ce20
--- /dev/null
+++ b/doc/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py
index d5a4b81003b1..19729fe8a388 100644
--- a/nova/tests/fake_policy.py
+++ b/nova/tests/fake_policy.py
@@ -192,6 +192,7 @@ policy_data = """
"compute_extension:v3:flavor-extra-specs:delete": "is_admin:True",
"compute_extension:flavormanage": "",
"compute_extension:v3:flavor-manage": "",
+ "compute_extension:v3:flavors:discoverable": "",
"compute_extension:floating_ip_dns": "",
"compute_extension:floating_ip_pools": "",
"compute_extension:floating_ips": "",
diff --git a/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.json.tpl b/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.json.tpl
new file mode 100644
index 000000000000..a042b02963de
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.json.tpl
@@ -0,0 +1,9 @@
+{
+ "extension": {
+ "alias": "flavors",
+ "description": "Flavors Extension.",
+ "name": "flavors",
+ "namespace": "http://docs.openstack.org/compute/core/flavors/v3",
+ "version": 1
+ }
+}
diff --git a/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.xml.tpl b/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.xml.tpl
new file mode 100644
index 000000000000..5d079b446e69
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/extension-info/extensions-get-resp.xml.tpl
@@ -0,0 +1,4 @@
+
+
+ Flavors Extension.
+
diff --git a/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.json.tpl b/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.json.tpl
new file mode 100644
index 000000000000..824937613efc
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.json.tpl
@@ -0,0 +1,11 @@
+{
+ "extensions": [
+ {
+ "alias": "flavors",
+ "description": "Flavors Extension.",
+ "name": "flavors",
+ "namespace": "http://docs.openstack.org/compute/core/flavors/v3",
+ "version": 1
+ }
+ ]
+}
diff --git a/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.xml.tpl b/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.xml.tpl
new file mode 100644
index 000000000000..6c0a6bfe241e
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/extension-info/extensions-list-resp.xml.tpl
@@ -0,0 +1,6 @@
+
+
+
+ Flavors Extension.
+
+
diff --git a/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.json.tpl
new file mode 100644
index 000000000000..52a2bbea98da
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.json.tpl
@@ -0,0 +1,27 @@
+{
+ "instance_action": {
+ "action": "%(action)s",
+ "instance_uuid": "%(instance_uuid)s",
+ "request_id": "%(request_id)s",
+ "user_id": "%(integer_id)s",
+ "project_id": "%(integer_id)s",
+ "start_time": "%(timestamp)s",
+ "message": "",
+ "events": [
+ {
+ "event": "%(event)s",
+ "start_time": "%(timestamp)s",
+ "finish_time": "%(timestamp)s",
+ "result": "%(result)s",
+ "traceback": ""
+ },
+ {
+ "event": "%(event)s",
+ "start_time": "%(timestamp)s",
+ "finish_time": "%(timestamp)s",
+ "result": "%(result)s",
+ "traceback": ""
+ }
+ ]
+ }
+}
diff --git a/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.xml.tpl b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.xml.tpl
new file mode 100644
index 000000000000..91b9cb97b947
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-action-get-resp.xml.tpl
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.json.tpl
new file mode 100644
index 000000000000..7ac78220b9c9
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.json.tpl
@@ -0,0 +1,22 @@
+{
+ "instance_actions": [
+ {
+ "action": "%(action)s",
+ "instance_uuid": "%(uuid)s",
+ "request_id": "%(request_id)s",
+ "user_id": "%(integer_id)s",
+ "project_id": "%(integer_id)s",
+ "start_time": "%(timestamp)s",
+ "message": ""
+ },
+ {
+ "action": "%(action)s",
+ "instance_uuid": "%(uuid)s",
+ "request_id": "%(request_id)s",
+ "user_id": "%(integer_id)s",
+ "project_id": "%(integer_id)s",
+ "start_time": "%(timestamp)s",
+ "message": ""
+ }
+ ]
+}
diff --git a/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml.tpl b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml.tpl
new file mode 100644
index 000000000000..053e5ee3ea9c
--- /dev/null
+++ b/nova/tests/integrated/v3/api_samples/os-instance-actions/instance-actions-list-resp.xml.tpl
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nova/tests/integrated/v3/test_extension_info.py b/nova/tests/integrated/v3/test_extension_info.py
new file mode 100644
index 000000000000..c55da00c02a8
--- /dev/null
+++ b/nova/tests/integrated/v3/test_extension_info.py
@@ -0,0 +1,35 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2012 Nebula, Inc.
+# Copyright 2013 IBM Corp.
+#
+# 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 nova.tests.integrated.v3 import api_sample_base
+
+
+class ExtensionInfoSamplesJsonTest(api_sample_base.ApiSampleTestBaseV3):
+ sample_dir = "extension-info"
+
+ def test_list_extensions(self):
+ response = self._do_get('extensions')
+ subs = self._get_regexes()
+ self._verify_response('extensions-list-resp', subs, response, 200)
+
+ def test_get_extensions(self):
+ response = self._do_get('extensions/flavors')
+ subs = self._get_regexes()
+ self._verify_response('extensions-get-resp', subs, response, 200)
+
+
+class ExtensionInfoSamplesXmlTest(ExtensionInfoSamplesJsonTest):
+ ctype = 'xml'
diff --git a/nova/tests/integrated/v3/test_instance_actions.py b/nova/tests/integrated/v3/test_instance_actions.py
new file mode 100644
index 000000000000..e910003c6216
--- /dev/null
+++ b/nova/tests/integrated/v3/test_instance_actions.py
@@ -0,0 +1,89 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2012 Nebula, Inc.
+# Copyright 2013 IBM Corp.
+#
+# 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.
+
+import copy
+
+from nova.compute import api as compute_api
+from nova import db
+from nova.tests import fake_instance_actions
+from nova.tests.integrated.v3 import api_sample_base
+from nova.tests import utils as test_utils
+
+
+class InstanceActionsSampleJsonTest(api_sample_base.ApiSampleTestBaseV3):
+ extension_name = 'os-instance-actions'
+
+ def setUp(self):
+ super(InstanceActionsSampleJsonTest, self).setUp()
+ self.actions = fake_instance_actions.FAKE_ACTIONS
+ self.events = fake_instance_actions.FAKE_EVENTS
+ self.instance = test_utils.get_test_instance()
+
+ def fake_instance_action_get_by_request_id(context, uuid, request_id):
+ return copy.deepcopy(self.actions[uuid][request_id])
+
+ def fake_instance_actions_get(context, uuid):
+ return [copy.deepcopy(value) for value in
+ self.actions[uuid].itervalues()]
+
+ def fake_instance_action_events_get(context, action_id):
+ return copy.deepcopy(self.events[action_id])
+
+ def fake_instance_get_by_uuid(context, instance_id):
+ return self.instance
+
+ def fake_get(self, context, instance_uuid):
+ return {'uuid': instance_uuid}
+
+ self.stubs.Set(db, 'action_get_by_request_id',
+ fake_instance_action_get_by_request_id)
+ self.stubs.Set(db, 'actions_get', fake_instance_actions_get)
+ self.stubs.Set(db, 'action_events_get',
+ fake_instance_action_events_get)
+ self.stubs.Set(db, 'instance_get_by_uuid', fake_instance_get_by_uuid)
+ self.stubs.Set(compute_api.API, 'get', fake_get)
+
+ def test_instance_action_get(self):
+ fake_uuid = fake_instance_actions.FAKE_UUID
+ fake_request_id = fake_instance_actions.FAKE_REQUEST_ID1
+ fake_action = self.actions[fake_uuid][fake_request_id]
+
+ response = self._do_get('servers/%s/os-instance-actions/%s' %
+ (fake_uuid, fake_request_id))
+ subs = self._get_regexes()
+ subs['action'] = '(reboot)|(resize)'
+ subs['instance_uuid'] = fake_uuid
+ subs['integer_id'] = '[0-9]+'
+ subs['request_id'] = fake_action['request_id']
+ subs['start_time'] = fake_action['start_time']
+ subs['result'] = '(Success)|(Error)'
+ subs['event'] = '(schedule)|(compute_create)'
+ self._verify_response('instance-action-get-resp', subs, response, 200)
+
+ def test_instance_actions_list(self):
+ fake_uuid = fake_instance_actions.FAKE_UUID
+ response = self._do_get('servers/%s/os-instance-actions' % (fake_uuid))
+ subs = self._get_regexes()
+ subs['action'] = '(reboot)|(resize)'
+ subs['integer_id'] = '[0-9]+'
+ subs['request_id'] = ('req-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}'
+ '-[0-9a-f]{4}-[0-9a-f]{12}')
+ self._verify_response('instance-actions-list-resp', subs,
+ response, 200)
+
+
+class InstanceActionsSampleXmlTest(InstanceActionsSampleJsonTest):
+ ctype = 'xml'