# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
#    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 iso8601
from lxml import etree
from oslo.config import cfg
import webob

from nova.api.openstack import compute
from nova.api.openstack.compute import extensions as compute_extensions
from nova.api.openstack import extensions as base_extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
from nova.openstack.common import jsonutils
import nova.policy
from nova import test
from nova.tests.api.openstack import fakes
from nova.tests import matchers

CONF = cfg.CONF

NS = "{http://docs.openstack.org/common/api/v1.0}"
ATOMNS = "{http://www.w3.org/2005/Atom}"
response_body = "Try to say this Mr. Knox, sir..."
extension_body = "I am not a fox!"


class StubController(object):

    def __init__(self, body):
        self.body = body

    def index(self, req):
        return self.body

    def create(self, req, body):
        msg = 'All aboard the fail train!'
        raise webob.exc.HTTPBadRequest(explanation=msg)

    def show(self, req, id):
        raise webob.exc.HTTPNotFound()


class StubActionController(wsgi.Controller):
    def __init__(self, body):
        self.body = body

    @wsgi.action('fooAction')
    def _action_foo(self, req, id, body):
        return self.body


class StubControllerExtension(base_extensions.ExtensionDescriptor):
    name = 'twaadle'

    def __init__(self):
        pass


class StubEarlyExtensionController(wsgi.Controller):
    def __init__(self, body):
        self.body = body

    @wsgi.extends
    def index(self, req):
        yield self.body

    @wsgi.extends(action='fooAction')
    def _action_foo(self, req, id, body):
        yield self.body


class StubLateExtensionController(wsgi.Controller):
    def __init__(self, body):
        self.body = body

    @wsgi.extends
    def index(self, req, resp_obj):
        return self.body

    @wsgi.extends(action='fooAction')
    def _action_foo(self, req, resp_obj, id, body):
        return self.body


class StubExtensionManager(object):
    """Provides access to Tweedle Beetles."""

    name = "Tweedle Beetle Extension"
    alias = "TWDLBETL"

    def __init__(self, resource_ext=None, action_ext=None, request_ext=None,
                 controller_ext=None):
        self.resource_ext = resource_ext
        self.action_ext = action_ext
        self.request_ext = request_ext
        self.controller_ext = controller_ext
        self.extra_resource_ext = None

    def get_resources(self):
        resource_exts = []
        if self.resource_ext:
            resource_exts.append(self.resource_ext)
        if self.extra_resource_ext:
            resource_exts.append(self.extra_resource_ext)
        return resource_exts

    def get_actions(self):
        action_exts = []
        if self.action_ext:
            action_exts.append(self.action_ext)
        return action_exts

    def get_request_extensions(self):
        request_extensions = []
        if self.request_ext:
            request_extensions.append(self.request_ext)
        return request_extensions

    def get_controller_extensions(self):
        controller_extensions = []
        if self.controller_ext:
            controller_extensions.append(self.controller_ext)
        return controller_extensions


class ExtensionTestCase(test.TestCase):
    def setUp(self):
        super(ExtensionTestCase, self).setUp()
        ext_list = CONF.osapi_compute_extension[:]
        fox = ('nova.tests.api.openstack.compute.extensions.'
               'foxinsocks.Foxinsocks')
        if fox not in ext_list:
            ext_list.append(fox)
            self.flags(osapi_compute_extension=ext_list)
        self.fake_context = nova.context.RequestContext('fake', 'fake')

    def test_extension_authorizer_throws_exception_if_policy_fails(self):
        target = {'project_id': '1234',
                  'user_id': '5678'}
        self.mox.StubOutWithMock(nova.policy, 'enforce')
        nova.policy.enforce(self.fake_context,
                            "compute_extension:used_limits_for_admin",
                            target).AndRaise(
            exception.PolicyNotAuthorized(
                action="compute_extension:used_limits_for_admin"))
        self.mox.ReplayAll()
        authorize = base_extensions.extension_authorizer('compute',
                                                        'used_limits_for_admin'
        )
        self.assertRaises(exception.PolicyNotAuthorized, authorize,
                          self.fake_context, target=target)

    def test_core_authorizer_throws_exception_if_policy_fails(self):
        target = {'project_id': '1234',
                  'user_id': '5678'}
        self.mox.StubOutWithMock(nova.policy, 'enforce')
        nova.policy.enforce(self.fake_context,
                            "compute:used_limits_for_admin",
                            target).AndRaise(
            exception.PolicyNotAuthorized(
                action="compute:used_limits_for_admin"))
        self.mox.ReplayAll()
        authorize = base_extensions.core_authorizer('compute',
                                                    'used_limits_for_admin'
        )
        self.assertRaises(exception.PolicyNotAuthorized, authorize,
                          self.fake_context, target=target)


class ExtensionControllerTest(ExtensionTestCase):

    def setUp(self):
        super(ExtensionControllerTest, self).setUp()
        self.ext_list = [
            "AdminActions",
            "Aggregates",
            "AssistedVolumeSnapshots",
            "AvailabilityZone",
            "Agents",
            "Certificates",
            "Cloudpipe",
            "CloudpipeUpdate",
            "ConsoleOutput",
            "Consoles",
            "Createserverext",
            "DeferredDelete",
            "DiskConfig",
            "ExtendedAvailabilityZone",
            "ExtendedFloatingIps",
            "ExtendedIps",
            "ExtendedIpsMac",
            "ExtendedVIFNet",
            "Evacuate",
            "ExtendedStatus",
            "ExtendedVolumes",
            "ExtendedServerAttributes",
            "FixedIPs",
            "FlavorAccess",
            "FlavorDisabled",
            "FlavorExtraSpecs",
            "FlavorExtraData",
            "FlavorManage",
            "FlavorRxtx",
            "FlavorSwap",
            "FloatingIps",
            "FloatingIpDns",
            "FloatingIpPools",
            "FloatingIpsBulk",
            "Fox In Socks",
            "Hosts",
            "ImageSize",
            "InstanceActions",
            "Keypairs",
            "Multinic",
            "MultipleCreate",
            "Quotas",
            "ExtendedQuotas",
            "Rescue",
            "SchedulerHints",
            "SecurityGroupDefaultRules",
            "SecurityGroups",
            "ServerDiagnostics",
            "ServerPassword",
            "ServerStartStop",
            "Services",
            "SimpleTenantUsage",
            "UsedLimits",
            "UserData",
            "VirtualInterfaces",
            "VolumeAttachmentUpdate",
            "Volumes",
            ]
        self.ext_list.sort()

    def test_list_extensions_json(self):
        app = compute.APIRouter(init_only=('extensions',))
        request = webob.Request.blank("/fake/extensions")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)

        # Make sure we have all the extensions, extra extensions being OK.
        data = jsonutils.loads(response.body)
        names = [str(x['name']) for x in data['extensions']
                 if str(x['name']) in self.ext_list]
        names.sort()
        self.assertEqual(names, self.ext_list)

        # Ensure all the timestamps are valid according to iso8601
        for ext in data['extensions']:
            iso8601.parse_date(ext['updated'])

        # Make sure that at least Fox in Sox is correct.
        (fox_ext, ) = [
            x for x in data['extensions'] if x['alias'] == 'FOXNSOX']
        self.assertEqual(fox_ext, {
                'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0',
                'name': 'Fox In Socks',
                'updated': '2011-01-22T13:25:27-06:00',
                'description': 'The Fox In Socks Extension.',
                'alias': 'FOXNSOX',
                'links': []
            },
        )

        for ext in data['extensions']:
            url = '/fake/extensions/%s' % ext['alias']
            request = webob.Request.blank(url)
            response = request.get_response(app)
            output = jsonutils.loads(response.body)
            self.assertEqual(output['extension']['alias'], ext['alias'])

    def test_get_extension_json(self):
        app = compute.APIRouter(init_only=('extensions',))
        request = webob.Request.blank("/fake/extensions/FOXNSOX")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)

        data = jsonutils.loads(response.body)
        self.assertEqual(data['extension'], {
                "namespace": "http://www.fox.in.socks/api/ext/pie/v1.0",
                "name": "Fox In Socks",
                "updated": "2011-01-22T13:25:27-06:00",
                "description": "The Fox In Socks Extension.",
                "alias": "FOXNSOX",
                "links": []})

    def test_get_non_existing_extension_json(self):
        app = compute.APIRouter(init_only=('extensions',))
        request = webob.Request.blank("/fake/extensions/4")
        response = request.get_response(app)
        self.assertEqual(404, response.status_int)

    def test_list_extensions_xml(self):
        app = compute.APIRouter(init_only=('servers', 'flavors', 'extensions'))
        request = webob.Request.blank("/fake/extensions")
        request.accept = "application/xml"
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)

        root = etree.XML(response.body)
        self.assertEqual(root.tag.split('extensions')[0], NS)

        # Make sure we have all the extensions, extras extensions being OK.
        exts = root.findall('{0}extension'.format(NS))
        self.assertTrue(len(exts) >= len(self.ext_list))

        # Make sure that at least Fox in Sox is correct.
        (fox_ext, ) = [x for x in exts if x.get('alias') == 'FOXNSOX']
        self.assertEqual(fox_ext.get('name'), 'Fox In Socks')
        self.assertEqual(fox_ext.get('namespace'),
            'http://www.fox.in.socks/api/ext/pie/v1.0')
        self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00')
        self.assertEqual(fox_ext.findtext('{0}description'.format(NS)),
            'The Fox In Socks Extension.')

        xmlutil.validate_schema(root, 'extensions')

    def test_get_extension_xml(self):
        app = compute.APIRouter(init_only=('servers', 'flavors', 'extensions'))
        request = webob.Request.blank("/fake/extensions/FOXNSOX")
        request.accept = "application/xml"
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        xml = response.body

        root = etree.XML(xml)
        self.assertEqual(root.tag.split('extension')[0], NS)
        self.assertEqual(root.get('alias'), 'FOXNSOX')
        self.assertEqual(root.get('name'), 'Fox In Socks')
        self.assertEqual(root.get('namespace'),
            'http://www.fox.in.socks/api/ext/pie/v1.0')
        self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00')
        self.assertEqual(root.findtext('{0}description'.format(NS)),
            'The Fox In Socks Extension.')

        xmlutil.validate_schema(root, 'extension')


class ResourceExtensionTest(ExtensionTestCase):

    def test_no_extension_present(self):
        manager = StubExtensionManager(None)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/blah")
        response = request.get_response(app)
        self.assertEqual(404, response.status_int)

    def test_get_resources(self):
        res_ext = base_extensions.ResourceExtension('tweedles',
                                  StubController(response_body))
        manager = StubExtensionManager(res_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(response_body, response.body)

    def test_get_resources_with_controller(self):
        res_ext = base_extensions.ResourceExtension('tweedles',
                                               StubController(response_body))
        manager = StubExtensionManager(res_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(response_body, response.body)

    def test_bad_request(self):
        res_ext = base_extensions.ResourceExtension('tweedles',
                                  StubController(response_body))
        manager = StubExtensionManager(res_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles")
        request.method = "POST"
        response = request.get_response(app)
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)
        body = jsonutils.loads(response.body)
        expected = {
            "badRequest": {
                "message": "All aboard the fail train!",
                "code": 400
            }
        }
        self.assertThat(expected, matchers.DictMatches(body))

    def test_non_exist_resource(self):
        res_ext = base_extensions.ResourceExtension('tweedles',
                                               StubController(response_body))
        manager = StubExtensionManager(res_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles/1")
        response = request.get_response(app)
        self.assertEqual(404, response.status_int)
        self.assertEqual('application/json', response.content_type)
        body = jsonutils.loads(response.body)
        expected = {
            "itemNotFound": {
                "message": "The resource could not be found.",
                "code": 404
            }
        }
        self.assertThat(expected, matchers.DictMatches(body))


class InvalidExtension(object):

    alias = "THIRD"


class ExtensionManagerTest(ExtensionTestCase):

    response_body = "Try to say this Mr. Knox, sir..."

    def test_get_resources(self):
        app = compute.APIRouter()
        request = webob.Request.blank("/fake/foxnsocks")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(response_body, response.body)

    def test_invalid_extensions(self):
        # Don't need the serialization middleware here because we're
        # not testing any serialization
        app = compute.APIRouter()
        ext_mgr = compute_extensions.ExtensionManager()
        ext_mgr.register(InvalidExtension())
        self.assertTrue(ext_mgr.is_loaded('FOXNSOX'))
        self.assertFalse(ext_mgr.is_loaded('THIRD'))


class ActionExtensionTest(ExtensionTestCase):

    def _send_server_action_request(self, url, body):
        app = compute.APIRouter(init_only=('servers',))
        request = webob.Request.blank(url)
        request.method = 'POST'
        request.content_type = 'application/json'
        request.body = jsonutils.dumps(body)
        response = request.get_response(app)
        return response

    def test_extended_action(self):
        body = dict(add_tweedle=dict(name="test"))
        url = "/fake/servers/abcd/action"
        response = self._send_server_action_request(url, body)
        self.assertEqual(200, response.status_int)
        self.assertEqual("Tweedle Beetle Added.", response.body)

        body = dict(delete_tweedle=dict(name="test"))
        response = self._send_server_action_request(url, body)
        self.assertEqual(200, response.status_int)
        self.assertEqual("Tweedle Beetle Deleted.", response.body)

    def test_invalid_action(self):
        body = dict(blah=dict(name="test"))  # Doesn't exist
        url = "/fake/servers/abcd/action"
        response = self._send_server_action_request(url, body)
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)
        body = jsonutils.loads(response.body)
        expected = {
            "badRequest": {
                "message": "There is no such action: blah",
                "code": 400
            }
        }
        self.assertThat(expected, matchers.DictMatches(body))

    def test_non_exist_action(self):
        body = dict(blah=dict(name="test"))
        url = "/fake/fdsa/1/action"
        response = self._send_server_action_request(url, body)
        self.assertEqual(404, response.status_int)

    def test_failed_action(self):
        body = dict(fail=dict(name="test"))
        url = "/fake/servers/abcd/action"
        response = self._send_server_action_request(url, body)
        self.assertEqual(400, response.status_int)
        self.assertEqual('application/json', response.content_type)
        body = jsonutils.loads(response.body)
        expected = {
            "badRequest": {
                "message": "Tweedle fail",
                "code": 400
            }
        }
        self.assertThat(expected, matchers.DictMatches(body))


class RequestExtensionTest(ExtensionTestCase):

    def test_get_resources_with_stub_mgr(self):
        class GooGoose(wsgi.Controller):
            @wsgi.extends
            def show(self, req, resp_obj, id):
                # only handle JSON responses
                resp_obj.obj['flavor']['googoose'] = req.GET.get('chewing')

        req_ext = base_extensions.ControllerExtension(
            StubControllerExtension(), 'flavors', GooGoose())

        manager = StubExtensionManager(None, None, None, req_ext)
        app = fakes.wsgi_app(ext_mgr=manager)
        request = webob.Request.blank("/v2/fake/flavors/1?chewing=bluegoo")
        request.environ['api.version'] = '2'
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        response_data = jsonutils.loads(response.body)
        self.assertEqual('bluegoo', response_data['flavor']['googoose'])

    def test_get_resources_with_mgr(self):

        app = fakes.wsgi_app(init_only=('flavors',))
        request = webob.Request.blank("/v2/fake/flavors/1?chewing=newblue")
        request.environ['api.version'] = '2'
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        response_data = jsonutils.loads(response.body)
        self.assertEqual('newblue', response_data['flavor']['googoose'])
        self.assertEqual("Pig Bands!", response_data['big_bands'])


class ControllerExtensionTest(ExtensionTestCase):
    def test_controller_extension_early(self):
        controller = StubController(response_body)
        res_ext = base_extensions.ResourceExtension('tweedles', controller)
        ext_controller = StubEarlyExtensionController(extension_body)
        extension = StubControllerExtension()
        cont_ext = base_extensions.ControllerExtension(extension, 'tweedles',
                                                       ext_controller)
        manager = StubExtensionManager(resource_ext=res_ext,
                                       controller_ext=cont_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(extension_body, response.body)

    def test_controller_extension_late(self):
        # Need a dict for the body to convert to a ResponseObject
        controller = StubController(dict(foo=response_body))
        res_ext = base_extensions.ResourceExtension('tweedles', controller)

        ext_controller = StubLateExtensionController(extension_body)
        extension = StubControllerExtension()
        cont_ext = base_extensions.ControllerExtension(extension, 'tweedles',
                                                       ext_controller)

        manager = StubExtensionManager(resource_ext=res_ext,
                                       controller_ext=cont_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(extension_body, response.body)

    def test_controller_extension_late_inherited_resource(self):
        # Need a dict for the body to convert to a ResponseObject
        controller = StubController(dict(foo=response_body))
        parent_ext = base_extensions.ResourceExtension('tweedles', controller)

        ext_controller = StubLateExtensionController(extension_body)
        extension = StubControllerExtension()
        cont_ext = base_extensions.ControllerExtension(extension, 'tweedles',
                                                       ext_controller)

        manager = StubExtensionManager(resource_ext=parent_ext,
                                       controller_ext=cont_ext)
        child_ext = base_extensions.ResourceExtension('beetles', controller,
                                                      inherits='tweedles')
        manager.extra_resource_ext = child_ext
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/beetles")
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(extension_body, response.body)

    def test_controller_action_extension_early(self):
        controller = StubActionController(response_body)
        actions = dict(action='POST')
        res_ext = base_extensions.ResourceExtension('tweedles', controller,
                                                    member_actions=actions)
        ext_controller = StubEarlyExtensionController(extension_body)
        extension = StubControllerExtension()
        cont_ext = base_extensions.ControllerExtension(extension, 'tweedles',
                                                       ext_controller)
        manager = StubExtensionManager(resource_ext=res_ext,
                                       controller_ext=cont_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles/foo/action")
        request.method = 'POST'
        request.headers['Content-Type'] = 'application/json'
        request.body = jsonutils.dumps(dict(fooAction=True))
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(extension_body, response.body)

    def test_controller_action_extension_late(self):
        # Need a dict for the body to convert to a ResponseObject
        controller = StubActionController(dict(foo=response_body))
        actions = dict(action='POST')
        res_ext = base_extensions.ResourceExtension('tweedles', controller,
                                                    member_actions=actions)

        ext_controller = StubLateExtensionController(extension_body)
        extension = StubControllerExtension()
        cont_ext = base_extensions.ControllerExtension(extension, 'tweedles',
                                                       ext_controller)

        manager = StubExtensionManager(resource_ext=res_ext,
                                       controller_ext=cont_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/tweedles/foo/action")
        request.method = 'POST'
        request.headers['Content-Type'] = 'application/json'
        request.body = jsonutils.dumps(dict(fooAction=True))
        response = request.get_response(app)
        self.assertEqual(200, response.status_int)
        self.assertEqual(extension_body, response.body)


class ExtensionsXMLSerializerTest(test.TestCase):

    def test_serialize_extension(self):
        serializer = base_extensions.ExtensionTemplate()
        data = {'extension': {
          'name': 'ext1',
          'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0',
          'alias': 'RS-PIE',
          'updated': '2011-01-22T13:25:27-06:00',
          'description': 'Adds the capability to share an image.',
          'links': [{'rel': 'describedby',
                     'type': 'application/pdf',
                     'href': 'http://docs.rack.com/servers/api/ext/cs.pdf'},
                    {'rel': 'describedby',
                     'type': 'application/vnd.sun.wadl+xml',
                     'href': 'http://docs.rack.com/servers/api/ext/cs.wadl'}]}}

        xml = serializer.serialize(data)
        root = etree.XML(xml)
        ext_dict = data['extension']
        self.assertEqual(root.findtext('{0}description'.format(NS)),
            ext_dict['description'])

        for key in ['name', 'namespace', 'alias', 'updated']:
            self.assertEqual(root.get(key), ext_dict[key])

        link_nodes = root.findall('{0}link'.format(ATOMNS))
        self.assertEqual(len(link_nodes), 2)
        for i, link in enumerate(ext_dict['links']):
            for key, value in link.items():
                self.assertEqual(link_nodes[i].get(key), value)

        xmlutil.validate_schema(root, 'extension')

    def test_serialize_extensions(self):
        serializer = base_extensions.ExtensionsTemplate()
        data = {"extensions": [{
                "name": "Public Image Extension",
                "namespace": "http://foo.com/api/ext/pie/v1.0",
                "alias": "RS-PIE",
                "updated": "2011-01-22T13:25:27-06:00",
                "description": "Adds the capability to share an image.",
                "links": [{"rel": "describedby",
                            "type": "application/pdf",
                            "type": "application/vnd.sun.wadl+xml",
                            "href": "http://foo.com/api/ext/cs-pie.pdf"},
                           {"rel": "describedby",
                            "type": "application/vnd.sun.wadl+xml",
                            "href": "http://foo.com/api/ext/cs-pie.wadl"}]},
                {"name": "Cloud Block Storage",
                 "namespace": "http://foo.com/api/ext/cbs/v1.0",
                 "alias": "RS-CBS",
                 "updated": "2011-01-12T11:22:33-06:00",
                 "description": "Allows mounting cloud block storage.",
                 "links": [{"rel": "describedby",
                             "type": "application/pdf",
                             "href": "http://foo.com/api/ext/cs-cbs.pdf"},
                            {"rel": "describedby",
                             "type": "application/vnd.sun.wadl+xml",
                             "href": "http://foo.com/api/ext/cs-cbs.wadl"}]}]}

        xml = serializer.serialize(data)
        root = etree.XML(xml)
        ext_elems = root.findall('{0}extension'.format(NS))
        self.assertEqual(len(ext_elems), 2)
        for i, ext_elem in enumerate(ext_elems):
            ext_dict = data['extensions'][i]
            self.assertEqual(ext_elem.findtext('{0}description'.format(NS)),
                ext_dict['description'])

            for key in ['name', 'namespace', 'alias', 'updated']:
                self.assertEqual(ext_elem.get(key), ext_dict[key])

            link_nodes = ext_elem.findall('{0}link'.format(ATOMNS))
            self.assertEqual(len(link_nodes), 2)
            for i, link in enumerate(ext_dict['links']):
                for key, value in link.items():
                    self.assertEqual(link_nodes[i].get(key), value)

        xmlutil.validate_schema(root, 'extensions')


class ExtensionControllerIdFormatTest(test.TestCase):

    def _bounce_id(self, test_id):

        class BounceController(object):
            def show(self, req, id):
                return id
        res_ext = base_extensions.ResourceExtension('bounce',
                                                    BounceController())
        manager = StubExtensionManager(res_ext)
        app = compute.APIRouter(manager)
        request = webob.Request.blank("/fake/bounce/%s" % test_id)
        response = request.get_response(app)
        return response.body

    def test_id_with_xml_format(self):
        result = self._bounce_id('foo.xml')
        self.assertEqual(result, 'foo')

    def test_id_with_json_format(self):
        result = self._bounce_id('foo.json')
        self.assertEqual(result, 'foo')

    def test_id_with_bad_format(self):
        result = self._bounce_id('foo.bad')
        self.assertEqual(result, 'foo.bad')
