# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010-2011 OpenStack LLC.
# 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 base64
import datetime
import json
import unittest
from xml.dom import minidom

import webob

from nova import context
from nova import db
from nova import exception
from nova import test
from nova import utils
import nova.api.openstack
from nova.api.openstack import create_instance_helper
from nova.api.openstack import servers
from nova.api.openstack import wsgi
import nova.compute.api
from nova.compute import instance_types
from nova.compute import power_state
import nova.db.api
import nova.scheduler.api
from nova.db.sqlalchemy.models import Instance
from nova.db.sqlalchemy.models import InstanceMetadata
import nova.image.fake
import nova.rpc
from nova.tests.api.openstack import common
from nova.tests.api.openstack import fakes


FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'


def fake_gen_uuid():
    return FAKE_UUID


def return_server_by_id(context, id):
    return stub_instance(id)


def return_server_by_uuid(context, uuid):
    id = 1
    return stub_instance(id, uuid=uuid)


def return_virtual_interface_by_instance(interfaces):
    def _return_virtual_interface_by_instance(context, instance_id):
        return interfaces
    return _return_virtual_interface_by_instance


def return_virtual_interface_instance_nonexistant(interfaces):
    def _return_virtual_interface_by_instance(context, instance_id):
        raise exception.InstanceNotFound(instance_id=instance_id)
    return _return_virtual_interface_by_instance


def return_server_with_attributes(**kwargs):
    def _return_server(context, id):
        return stub_instance(id, **kwargs)
    return _return_server


def return_server_with_addresses(private, public):
    def _return_server(context, id):
        return stub_instance(id, private_address=private,
                             public_addresses=public)
    return _return_server


def return_server_with_power_state(power_state):
    def _return_server(context, id):
        return stub_instance(id, power_state=power_state)
    return _return_server


def return_server_with_uuid_and_power_state(power_state):
    def _return_server(context, id):
        return stub_instance(id, uuid=FAKE_UUID, power_state=power_state)
    return _return_server


def return_servers(context, *args, **kwargs):
    return [stub_instance(i, 'fake', 'fake') for i in xrange(5)]


def return_servers_by_reservation(context, reservation_id=""):
    return [stub_instance(i, reservation_id) for i in xrange(5)]


def return_servers_by_reservation_empty(context, reservation_id=""):
    return []


def return_servers_from_child_zones_empty(*args, **kwargs):
    return []


def return_servers_from_child_zones(*args, **kwargs):
    class Server(object):
        pass

    zones = []
    for zone in xrange(3):
        servers = []
        for server_id in xrange(5):
            server = Server()
            server._info = stub_instance(server_id, reservation_id="child")
            servers.append(server)

        zones.append(("Zone%d" % zone, servers))
    return zones


def return_security_group(context, instance_id, security_group_id):
    pass


def instance_update(context, instance_id, values):
    return stub_instance(instance_id, name=values.get('display_name'))


def instance_addresses(context, instance_id):
    return None


def stub_instance(id, user_id='fake', project_id='fake', private_address=None,
                  public_addresses=None, host=None, power_state=0,
                  reservation_id="", uuid=FAKE_UUID, image_ref="10",
                  flavor_id="1", interfaces=None, name=None):
    metadata = []
    metadata.append(InstanceMetadata(key='seq', value=id))

    if interfaces is None:
        interfaces = []

    inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id))

    if public_addresses is None:
        public_addresses = list()

    if host is not None:
        host = str(host)

    # ReservationID isn't sent back, hack it in there.
    server_name = name or "server%s" % id
    if reservation_id != "":
        server_name = "reservation_%s" % (reservation_id, )

    instance = {
        "id": int(id),
        "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
        "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
        "admin_pass": "",
        "user_id": user_id,
        "project_id": project_id,
        "image_ref": image_ref,
        "kernel_id": "",
        "ramdisk_id": "",
        "launch_index": 0,
        "key_name": "",
        "key_data": "",
        "state": power_state,
        "state_description": "",
        "memory_mb": 0,
        "vcpus": 0,
        "local_gb": 0,
        "hostname": "",
        "host": host,
        "instance_type": dict(inst_type),
        "user_data": "",
        "reservation_id": reservation_id,
        "mac_address": "",
        "scheduled_at": utils.utcnow(),
        "launched_at": utils.utcnow(),
        "terminated_at": utils.utcnow(),
        "availability_zone": "",
        "display_name": server_name,
        "display_description": "",
        "locked": False,
        "metadata": metadata,
        "uuid": uuid,
        "virtual_interfaces": interfaces}

    instance["fixed_ips"] = {
        "address": private_address,
        "floating_ips": [{"address":ip} for ip in public_addresses]}

    return instance


def fake_compute_api(cls, req, id):
    return True


def find_host(self, context, instance_id):
    return "nova"


class MockSetAdminPassword(object):
    def __init__(self):
        self.instance_id = None
        self.password = None

    def __call__(self, context, instance_id, password):
        self.instance_id = instance_id
        self.password = password


class ServersTest(test.TestCase):

    def setUp(self):
        self.maxDiff = None
        super(ServersTest, self).setUp()
        self.flags(verbose=True)
        fakes.stub_out_networking(self.stubs)
        fakes.stub_out_rate_limiting(self.stubs)
        fakes.stub_out_key_pair_funcs(self.stubs)
        fakes.stub_out_image_service(self.stubs)
        self.stubs.Set(utils, 'gen_uuid', fake_gen_uuid)
        self.stubs.Set(nova.db.api, 'instance_get_all_by_filters',
                return_servers)
        self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
        self.stubs.Set(nova.db, 'instance_get_by_uuid',
                       return_server_by_uuid)
        self.stubs.Set(nova.db.api, 'instance_get_all_by_project',
                       return_servers)
        self.stubs.Set(nova.db.api, 'instance_add_security_group',
                       return_security_group)
        self.stubs.Set(nova.db.api, 'instance_update', instance_update)
        self.stubs.Set(nova.db.api, 'instance_get_fixed_addresses',
                       instance_addresses)
        self.stubs.Set(nova.db.api, 'instance_get_floating_address',
                       instance_addresses)
        self.stubs.Set(nova.compute.API, 'pause', fake_compute_api)
        self.stubs.Set(nova.compute.API, 'unpause', fake_compute_api)
        self.stubs.Set(nova.compute.API, 'suspend', fake_compute_api)
        self.stubs.Set(nova.compute.API, 'resume', fake_compute_api)
        self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api)
        self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)

        self.webreq = common.webob_factory('/v1.0/servers')

    def test_get_server_by_id(self):
        req = webob.Request.blank('/v1.0/servers/1')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server1')

    def test_get_server_by_uuid(self):
        """
        The steps involved with resolving a UUID are pretty complicated;
        here's what's happening in this scenario:

        1. Show is calling `routing_get`

        2. `routing_get` is wrapped by `reroute_compute` which does the work
           of resolving requests to child zones.

        3. `reroute_compute` looks up the UUID by hitting the stub
           (returns_server_by_uuid)

        4. Since the stub return that the record exists, `reroute_compute`
           considers the request to be 'zone local', so it replaces the UUID
           in the argument list with an integer ID and then calls the inner
           function ('get').

        5. The call to `get` hits the other stub 'returns_server_by_id` which
           has the UUID set to FAKE_UUID

        So, counterintuitively, we call `get` twice on the `show` command.
        """
        req = webob.Request.blank('/v1.0/servers/%s' % FAKE_UUID)
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['uuid'], FAKE_UUID)
        self.assertEqual(res_dict['server']['name'], 'server1')

    def test_get_server_by_id_v1_1(self):
        image_bookmark = "http://localhost/images/10"
        flavor_ref = "http://localhost/v1.1/flavors/1"
        flavor_id = "1"
        flavor_bookmark = "http://localhost/flavors/1"

        public_ip = '192.168.0.3'
        private_ip = '172.19.0.1'
        interfaces = [
            {
                'network': {'label': 'public'},
                'fixed_ips': [
                    {'address': public_ip},
                ],
            },
            {
                'network': {'label': 'private'},
                'fixed_ips': [
                    {'address': private_ip},
                ],
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        expected_server = {
            "server": {
                "id": 1,
                "uuid": FAKE_UUID,
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 0,
                "name": "server1",
                "status": "BUILD",
                "hostId": '',
                "image": {
                    "id": "10",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                  "links": [
                                            {
                          "rel": "bookmark",
                          "href": flavor_bookmark,
                      },
                  ],
                },
                "addresses": {
                    "public": [
                        {
                            "version": 4,
                            "addr": public_ip,
                        },
                    ],
                    "private": [
                        {
                            "version": 4,
                            "addr": private_ip,
                        },
                    ],
                },
                "metadata": {
                    "seq": "1",
                },
                "links": [
                    {
                        "rel": "self",
                        #FIXME(wwolf) Do we want the links to be id or uuid?
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        self.assertDictMatch(res_dict, expected_server)

    def test_get_server_by_id_v1_1_xml(self):
        image_bookmark = "http://localhost/images/10"
        flavor_ref = "http://localhost/v1.1/flavors/1"
        flavor_id = "1"
        flavor_bookmark = "http://localhost/flavors/1"
        server_href = "http://localhost/v1.1/servers/1"
        server_bookmark = "http://localhost/servers/1"

        public_ip = '192.168.0.3'
        private_ip = '172.19.0.1'
        interfaces = [
            {
                'network': {'label': 'public'},
                'fixed_ips': [
                    {'address': public_ip},
                ],
            },
            {
                'network': {'label': 'private'},
                'fixed_ips': [
                    {'address': private_ip},
                ],
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        actual = minidom.parseString(res.body.replace('  ', ''))
        expected_uuid = FAKE_UUID
        expected_updated = "2010-11-11T11:00:00Z"
        expected_created = "2010-10-10T12:00:00Z"
        expected = minidom.parseString("""
        <server id="1"
                uuid="%(expected_uuid)s"
                xmlns="http://docs.openstack.org/compute/api/v1.1"
                xmlns:atom="http://www.w3.org/2005/Atom"
                name="server1"
                updated="%(expected_updated)s"
                created="%(expected_created)s"
                hostId=""
                status="BUILD"
                progress="0">
            <atom:link href="%(server_href)s" rel="self"/>
            <atom:link href="%(server_bookmark)s" rel="bookmark"/>
            <image id="10">
                <atom:link rel="bookmark" href="%(image_bookmark)s"/>
            </image>
            <flavor id="1">
                <atom:link rel="bookmark" href="%(flavor_bookmark)s"/>
            </flavor>
            <metadata>
                <meta key="seq">
                    1
                </meta>
            </metadata>
            <addresses>
                <network id="public">
                    <ip version="4" addr="%(public_ip)s"/>
                </network>
                <network id="private">
                    <ip version="4" addr="%(private_ip)s"/>
                </network>
            </addresses>
        </server>
        """.replace("  ", "") % (locals()))

        self.assertEqual(expected.toxml(), actual.toxml())

    def test_get_server_with_active_status_by_id_v1_1(self):
        image_bookmark = "http://localhost/images/10"
        flavor_ref = "http://localhost/v1.1/flavors/1"
        flavor_id = "1"
        flavor_bookmark = "http://localhost/flavors/1"
        private_ip = "192.168.0.3"
        public_ip = "1.2.3.4"

        interfaces = [
            {
                'network': {'label': 'public'},
                'fixed_ips': [
                    {'address': public_ip},
                ],
            },
            {
                'network': {'label': 'private'},
                'fixed_ips': [
                    {'address': private_ip},
                ],
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces, power_state=1)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        expected_server = {
            "server": {
                "id": 1,
                "uuid": FAKE_UUID,
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 100,
                "name": "server1",
                "status": "ACTIVE",
                "hostId": '',
                "image": {
                    "id": "10",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                  "links": [
                      {
                          "rel": "bookmark",
                          "href": flavor_bookmark,
                      },
                  ],
                },
                "addresses": {
                    "public": [
                        {
                            "version": 4,
                            "addr": public_ip,
                        },
                    ],
                    "private": [
                        {
                            "version": 4,
                            "addr": private_ip,
                        },
                    ],
                },
                "metadata": {
                    "seq": "1",
                },
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        self.assertDictMatch(res_dict, expected_server)

    def test_get_server_with_id_image_ref_by_id_v1_1(self):
        image_ref = "10"
        image_bookmark = "http://localhost/images/10"
        flavor_ref = "http://localhost/v1.1/flavors/1"
        flavor_id = "1"
        flavor_bookmark = "http://localhost/flavors/1"
        private_ip = "192.168.0.3"
        public_ip = "1.2.3.4"

        interfaces = [
            {
                'network': {'label': 'public'},
                'fixed_ips': [
                    {'address': public_ip},
                ],
            },
            {
                'network': {'label': 'private'},
                'fixed_ips': [
                    {'address': private_ip},
                ],
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces, power_state=1, image_ref=image_ref,
            flavor_id=flavor_id)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        expected_server = {
            "server": {
                "id": 1,
                "uuid": FAKE_UUID,
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 100,
                "name": "server1",
                "status": "ACTIVE",
                "hostId": '',
                "image": {
                    "id": "10",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                  "links": [
                      {
                          "rel": "bookmark",
                          "href": flavor_bookmark,
                      },
                  ],
                },
                "addresses": {
                    "public": [
                        {
                            "version": 4,
                            "addr": public_ip,
                        },
                    ],
                    "private": [
                        {
                            "version": 4,
                            "addr": private_ip,
                        },
                    ],
                },
                "metadata": {
                    "seq": "1",
                },
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        self.assertDictMatch(res_dict, expected_server)

    def test_get_server_by_id_with_addresses_xml(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        dom = minidom.parseString(res.body)
        server = dom.childNodes[0]
        self.assertEquals(server.nodeName, 'server')
        self.assertEquals(server.getAttribute('id'), '1')
        self.assertEquals(server.getAttribute('name'), 'server1')
        (public,) = server.getElementsByTagName('public')
        (ip,) = public.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'), '1.2.3.4')
        (private,) = server.getElementsByTagName('private')
        (ip,) = private.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'),  '192.168.0.3')

    def test_get_server_by_id_with_addresses(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server1')
        addresses = res_dict['server']['addresses']
        self.assertEqual(len(addresses["public"]), len(public))
        self.assertEqual(addresses["public"][0], public[0])
        self.assertEqual(len(addresses["private"]), 1)
        self.assertEqual(addresses["private"][0], private)

    def test_get_server_addresses_v1_0(self):
        private = '192.168.0.3'
        public = ['1.2.3.4']
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict, {
            'addresses': {'public': public, 'private': [private]}})

    def test_get_server_addresses_xml_v1_0(self):
        private_expected = "192.168.0.3"
        public_expected = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private_expected,
                                                         public_expected)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        dom = minidom.parseString(res.body)
        (addresses,) = dom.childNodes
        self.assertEquals(addresses.nodeName, 'addresses')
        (public,) = addresses.getElementsByTagName('public')
        (ip,) = public.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'), public_expected[0])
        (private,) = addresses.getElementsByTagName('private')
        (ip,) = private.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'), private_expected)

    def test_get_server_addresses_public_v1_0(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips/public')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict, {'public': public})

    def test_get_server_addresses_private_v1_0(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips/private')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict, {'private': [private]})

    def test_get_server_addresses_public_xml_v1_0(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips/public')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        dom = minidom.parseString(res.body)
        (public_node,) = dom.childNodes
        self.assertEquals(public_node.nodeName, 'public')
        (ip,) = public_node.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'), public[0])

    def test_get_server_addresses_private_xml_v1_0(self):
        private = "192.168.0.3"
        public = ["1.2.3.4"]
        new_return_server = return_server_with_addresses(private, public)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
        req = webob.Request.blank('/v1.0/servers/1/ips/private')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        dom = minidom.parseString(res.body)
        (private_node,) = dom.childNodes
        self.assertEquals(private_node.nodeName, 'private')
        (ip,) = private_node.getElementsByTagName('ip')
        self.assertEquals(ip.getAttribute('addr'), private)

    def test_get_server_by_id_with_addresses_v1_1(self):
        self.flags(use_ipv6=True)
        interfaces = [
            {
                'network': {'label': 'network_1'},
                'fixed_ips': [
                    {'address': '192.168.0.3'},
                    {'address': '192.168.0.4'},
                ],
            },
            {
                'network': {'label': 'network_2'},
                'fixed_ips': [
                    {'address': '172.19.0.1'},
                    {'address': '172.19.0.2'},
                ],
                'fixed_ipv6': '2001:4860::12',
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        res = req.get_response(fakes.wsgi_app())

        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server1')
        addresses = res_dict['server']['addresses']
        expected = {
            'network_1': [
                {'addr': '192.168.0.3', 'version': 4},
                {'addr': '192.168.0.4', 'version': 4},
            ],
            'network_2': [
                {'addr': '172.19.0.1', 'version': 4},
                {'addr': '172.19.0.2', 'version': 4},
                {'addr': '2001:4860::12', 'version': 6},
            ],
        }

        self.assertEqual(addresses, expected)

    def test_get_server_by_id_with_addresses_v1_1_ipv6_disabled(self):
        self.flags(use_ipv6=False)
        interfaces = [
            {
                'network': {'label': 'network_1'},
                'fixed_ips': [
                    {'address': '192.168.0.3'},
                    {'address': '192.168.0.4'},
                ],
            },
            {
                'network': {'label': 'network_2'},
                'fixed_ips': [
                    {'address': '172.19.0.1'},
                    {'address': '172.19.0.2'},
                ],
                'fixed_ipv6': '2001:4860::12',
            },
        ]
        new_return_server = return_server_with_attributes(
            interfaces=interfaces)
        self.stubs.Set(nova.db.api, 'instance_get', new_return_server)

        req = webob.Request.blank('/v1.1/servers/1')
        res = req.get_response(fakes.wsgi_app())

        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server1')
        addresses = res_dict['server']['addresses']
        expected = {
            'network_1': [
                {'addr': '192.168.0.3', 'version': 4},
                {'addr': '192.168.0.4', 'version': 4},
            ],
            'network_2': [
                {'addr': '172.19.0.1', 'version': 4},
                {'addr': '172.19.0.2', 'version': 4},
            ],
        }

        self.assertEqual(addresses, expected)

    def test_get_server_addresses_v1_1(self):
        self.flags(use_ipv6=True)
        interfaces = [
            {
                'network': {'label': 'network_1'},
                'fixed_ips': [
                    {'address': '192.168.0.3'},
                    {'address': '192.168.0.4'},
                ],
            },
            {
                'network': {'label': 'network_2'},
                'fixed_ips': [
                    {
                        'address': '172.19.0.1',
                        'floating_ips': [
                            {'address': '1.2.3.4'},
                        ],
                    },
                    {'address': '172.19.0.2'},
                ],
                'fixed_ipv6': '2001:4860::12',
            },
        ]

        _return_vifs = return_virtual_interface_by_instance(interfaces)
        self.stubs.Set(nova.db.api,
                       'virtual_interface_get_by_instance',
                       _return_vifs)

        req = webob.Request.blank('/v1.1/servers/1/ips')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        expected = {
            'addresses': {
                'network_1': [
                    {'version': 4, 'addr': '192.168.0.3'},
                    {'version': 4, 'addr': '192.168.0.4'},
                ],
                'network_2': [
                    {'version': 4, 'addr': '172.19.0.1'},
                    {'version': 4, 'addr': '1.2.3.4'},
                    {'version': 4, 'addr': '172.19.0.2'},
                    {'version': 6, 'addr': '2001:4860::12'},
                ],
            },
        }

        self.assertEqual(res_dict, expected)

    def test_get_server_addresses_single_network_v1_1(self):
        self.flags(use_ipv6=True)
        interfaces = [
            {
                'network': {'label': 'network_1'},
                'fixed_ips': [
                    {'address': '192.168.0.3'},
                    {'address': '192.168.0.4'},
                ],
            },
            {
                'network': {'label': 'network_2'},
                'fixed_ips': [
                    {
                        'address': '172.19.0.1',
                        'floating_ips': [
                            {'address': '1.2.3.4'},
                        ],
                    },
                    {'address': '172.19.0.2'},
                ],
                'fixed_ipv6': '2001:4860::12',
            },
        ]
        _return_vifs = return_virtual_interface_by_instance(interfaces)
        self.stubs.Set(nova.db.api,
                       'virtual_interface_get_by_instance',
                       _return_vifs)

        req = webob.Request.blank('/v1.1/servers/1/ips/network_2')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)
        expected = {
            'network_2': [
                {'version': 4, 'addr': '172.19.0.1'},
                {'version': 4, 'addr': '1.2.3.4'},
                {'version': 4, 'addr': '172.19.0.2'},
                {'version': 6, 'addr': '2001:4860::12'},
            ],
        }
        self.assertEqual(res_dict, expected)

    def test_get_server_addresses_nonexistant_network_v1_1(self):
        _return_vifs = return_virtual_interface_by_instance([])
        self.stubs.Set(nova.db.api,
                       'virtual_interface_get_by_instance',
                       _return_vifs)

        req = webob.Request.blank('/v1.1/servers/1/ips/network_0')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 404)

    def test_get_server_addresses_nonexistant_server_v1_1(self):
        _return_vifs = return_virtual_interface_instance_nonexistant([])
        self.stubs.Set(nova.db.api,
                       'virtual_interface_get_by_instance',
                       _return_vifs)

        req = webob.Request.blank('/v1.1/servers/600/ips')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 404)

    def test_get_server_list(self):
        req = webob.Request.blank('/v1.0/servers')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        self.assertEqual(len(res_dict['servers']), 5)
        i = 0
        for s in res_dict['servers']:
            self.assertEqual(s['id'], i)
            self.assertEqual(s['name'], 'server%d' % i)
            self.assertEqual(s.get('imageId', None), None)
            i += 1

    def test_get_server_list_with_reservation_id(self):
        self.stubs.Set(nova.db.api, 'instance_get_all_by_reservation',
                       return_servers_by_reservation)
        self.stubs.Set(nova.scheduler.api, 'call_zone_method',
                       return_servers_from_child_zones)
        req = webob.Request.blank('/v1.0/servers?reservation_id=foo')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        i = 0
        for s in res_dict['servers']:
            if '_is_precooked' in s:
                self.assertEqual(s.get('reservation_id'), 'child')
            else:
                self.assertEqual(s.get('name'), 'server%d' % i)
                i += 1

    def test_get_server_list_with_reservation_id_empty(self):
        self.stubs.Set(nova.db.api, 'instance_get_all_by_reservation',
                       return_servers_by_reservation_empty)
        self.stubs.Set(nova.scheduler.api, 'call_zone_method',
                       return_servers_from_child_zones_empty)
        req = webob.Request.blank('/v1.0/servers/detail?reservation_id=foo')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        i = 0
        for s in res_dict['servers']:
            if '_is_precooked' in s:
                self.assertEqual(s.get('reservation_id'), 'child')
            else:
                self.assertEqual(s.get('name'), 'server%d' % i)
                i += 1

    def test_get_server_list_with_reservation_id_details(self):
        self.stubs.Set(nova.db.api, 'instance_get_all_by_reservation',
                       return_servers_by_reservation)
        self.stubs.Set(nova.scheduler.api, 'call_zone_method',
                       return_servers_from_child_zones)
        req = webob.Request.blank('/v1.0/servers/detail?reservation_id=foo')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        i = 0
        for s in res_dict['servers']:
            if '_is_precooked' in s:
                self.assertEqual(s.get('reservation_id'), 'child')
            else:
                self.assertEqual(s.get('name'), 'server%d' % i)
                i += 1

    def test_get_server_list_v1_1(self):
        req = webob.Request.blank('/v1.1/servers')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        self.assertEqual(len(res_dict['servers']), 5)
        for i, s in enumerate(res_dict['servers']):
            self.assertEqual(s['id'], i)
            self.assertEqual(s['name'], 'server%d' % i)
            self.assertEqual(s.get('image', None), None)

            expected_links = [
                {
                    "rel": "self",
                    "href": "http://localhost/v1.1/servers/%s" % s['id'],
                },
                {
                    "rel": "bookmark",
                    "href": "http://localhost/servers/%s" % s['id'],
                },
            ]

            self.assertEqual(s['links'], expected_links)

    def test_get_servers_with_limit(self):
        req = webob.Request.blank('/v1.0/servers?limit=3')
        res = req.get_response(fakes.wsgi_app())
        servers = json.loads(res.body)['servers']
        self.assertEqual([s['id'] for s in servers], [0, 1, 2])

        req = webob.Request.blank('/v1.0/servers?limit=aaa')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)
        self.assertTrue('limit' in res.body)

    def test_get_servers_with_offset(self):
        req = webob.Request.blank('/v1.0/servers?offset=2')
        res = req.get_response(fakes.wsgi_app())
        servers = json.loads(res.body)['servers']
        self.assertEqual([s['id'] for s in servers], [2, 3, 4])

        req = webob.Request.blank('/v1.0/servers?offset=aaa')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)
        self.assertTrue('offset' in res.body)

    def test_get_servers_with_limit_and_offset(self):
        req = webob.Request.blank('/v1.0/servers?limit=2&offset=1')
        res = req.get_response(fakes.wsgi_app())
        servers = json.loads(res.body)['servers']
        self.assertEqual([s['id'] for s in servers], [1, 2])

    def test_get_servers_with_bad_limit(self):
        req = webob.Request.blank('/v1.0/servers?limit=asdf&offset=1')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)
        self.assertTrue(res.body.find('limit param') > -1)

    def test_get_servers_with_bad_offset(self):
        req = webob.Request.blank('/v1.0/servers?limit=2&offset=asdf')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)
        self.assertTrue(res.body.find('offset param') > -1)

    def test_get_servers_with_marker(self):
        req = webob.Request.blank('/v1.1/servers?marker=2')
        res = req.get_response(fakes.wsgi_app())
        servers = json.loads(res.body)['servers']
        self.assertEqual([s['name'] for s in servers], ["server3", "server4"])

    def test_get_servers_with_limit_and_marker(self):
        req = webob.Request.blank('/v1.1/servers?limit=2&marker=1')
        res = req.get_response(fakes.wsgi_app())
        servers = json.loads(res.body)['servers']
        self.assertEqual([s['name'] for s in servers], ['server2', 'server3'])

    def test_get_servers_with_bad_marker(self):
        req = webob.Request.blank('/v1.1/servers?limit=2&marker=asdf')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)
        self.assertTrue(res.body.find('marker param') > -1)

    def test_get_servers_with_bad_option_v1_0(self):
        # 1.0 API ignores unknown options
        def fake_get_all(compute_self, context, search_opts=None):
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        req = webob.Request.blank('/v1.0/servers?unknownoption=whee')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_with_bad_option_v1_1(self):
        # 1.1 API also ignores unknown options
        def fake_get_all(compute_self, context, search_opts=None):
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        req = webob.Request.blank('/v1.1/servers?unknownoption=whee')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_allows_image_v1_1(self):
        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('image' in search_opts)
            self.assertEqual(search_opts['image'], '12345')
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
        self.flags(allow_admin_api=False)

        req = webob.Request.blank('/v1.1/servers?image=12345')
        res = req.get_response(fakes.wsgi_app())
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_allows_flavor_v1_1(self):
        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('flavor' in search_opts)
            # flavor is an integer ID
            self.assertEqual(search_opts['flavor'], '12345')
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
        self.flags(allow_admin_api=False)

        req = webob.Request.blank('/v1.1/servers?flavor=12345')
        res = req.get_response(fakes.wsgi_app())
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_allows_status_v1_1(self):
        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('state' in search_opts)
            self.assertEqual(set(search_opts['state']),
                    set([power_state.RUNNING, power_state.BLOCKED]))
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
        self.flags(allow_admin_api=False)

        req = webob.Request.blank('/v1.1/servers?status=active')
        res = req.get_response(fakes.wsgi_app())
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_invalid_status_v1_1(self):
        """Test getting servers by invalid status"""

        self.flags(allow_admin_api=False)

        req = webob.Request.blank('/v1.1/servers?status=running')
        res = req.get_response(fakes.wsgi_app())
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 400)
        self.assertTrue(res.body.find('Invalid server status') > -1)

    def test_get_servers_allows_name_v1_1(self):
        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('name' in search_opts)
            self.assertEqual(search_opts['name'], 'whee.*')
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
        self.flags(allow_admin_api=False)

        req = webob.Request.blank('/v1.1/servers?name=whee.*')
        res = req.get_response(fakes.wsgi_app())
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_unknown_or_admin_options1(self):
        """Test getting servers by admin-only or unknown options.
        This tests when admin_api is off.  Make sure the admin and
        unknown options are stripped before they get to
        compute_api.get_all()
        """

        self.flags(allow_admin_api=False)

        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            # Allowed by user
            self.assertTrue('name' in search_opts)
            self.assertTrue('status' in search_opts)
            # Allowed only by admins with admin API on
            self.assertFalse('ip' in search_opts)
            self.assertFalse('unknown_option' in search_opts)
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        query_str = "name=foo&ip=10.*&status=active&unknown_option=meow"
        req = webob.Request.blank('/v1.1/servers?%s' % query_str)
        # Request admin context
        context = nova.context.RequestContext('testuser', 'testproject',
                is_admin=True)
        res = req.get_response(fakes.wsgi_app(fake_auth_context=context))
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_unknown_or_admin_options2(self):
        """Test getting servers by admin-only or unknown options.
        This tests when admin_api is on, but context is a user.
        Make sure the admin and unknown options are stripped before
        they get to compute_api.get_all()
        """

        self.flags(allow_admin_api=True)

        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            # Allowed by user
            self.assertTrue('name' in search_opts)
            self.assertTrue('status' in search_opts)
            # Allowed only by admins with admin API on
            self.assertFalse('ip' in search_opts)
            self.assertFalse('unknown_option' in search_opts)
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        query_str = "name=foo&ip=10.*&status=active&unknown_option=meow"
        req = webob.Request.blank('/v1.1/servers?%s' % query_str)
        # Request admin context
        context = nova.context.RequestContext('testuser', 'testproject',
                is_admin=False)
        res = req.get_response(fakes.wsgi_app(fake_auth_context=context))
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_unknown_or_admin_options3(self):
        """Test getting servers by admin-only or unknown options.
        This tests when admin_api is on and context is admin.
        All options should be passed through to compute_api.get_all()
        """

        self.flags(allow_admin_api=True)

        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            # Allowed by user
            self.assertTrue('name' in search_opts)
            self.assertTrue('status' in search_opts)
            # Allowed only by admins with admin API on
            self.assertTrue('ip' in search_opts)
            self.assertTrue('unknown_option' in search_opts)
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        query_str = "name=foo&ip=10.*&status=active&unknown_option=meow"
        req = webob.Request.blank('/v1.1/servers?%s' % query_str)
        # Request admin context
        context = nova.context.RequestContext('testuser', 'testproject',
                is_admin=True)
        res = req.get_response(fakes.wsgi_app(fake_auth_context=context))
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_admin_allows_ip_v1_1(self):
        """Test getting servers by ip with admin_api enabled and
        admin context
        """
        self.flags(allow_admin_api=True)

        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('ip' in search_opts)
            self.assertEqual(search_opts['ip'], '10\..*')
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        req = webob.Request.blank('/v1.1/servers?ip=10\..*')
        # Request admin context
        context = nova.context.RequestContext('testuser', 'testproject',
                is_admin=True)
        res = req.get_response(fakes.wsgi_app(fake_auth_context=context))
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def test_get_servers_admin_allows_ip6_v1_1(self):
        """Test getting servers by ip6 with admin_api enabled and
        admin context
        """
        self.flags(allow_admin_api=True)

        def fake_get_all(compute_self, context, search_opts=None):
            self.assertNotEqual(search_opts, None)
            self.assertTrue('ip6' in search_opts)
            self.assertEqual(search_opts['ip6'], 'ffff.*')
            return [stub_instance(100)]

        self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)

        req = webob.Request.blank('/v1.1/servers?ip6=ffff.*')
        # Request admin context
        context = nova.context.RequestContext('testuser', 'testproject',
                is_admin=True)
        res = req.get_response(fakes.wsgi_app(fake_auth_context=context))
        # The following assert will fail if either of the asserts in
        # fake_get_all() fail
        self.assertEqual(res.status_int, 200)
        servers = json.loads(res.body)['servers']
        self.assertEqual(len(servers), 1)
        self.assertEqual(servers[0]['id'], 100)

    def _setup_for_create_instance(self):
        """Shared implementation for tests below that create instance"""
        def instance_create(context, inst):
            inst_type = instance_types.get_instance_type_by_flavor_id(3)
            image_ref = 'http://localhost/images/2'
            return {'id': 1,
                    'display_name': 'server_test',
                    'uuid': FAKE_UUID,
                    'instance_type': dict(inst_type),
                    'image_ref': image_ref,
                    "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
                    "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
                   }

        def server_update(context, id, params):
            return instance_create(context, id)

        def fake_method(*args, **kwargs):
            pass

        def project_get_networks(context, user_id):
            return dict(id='1', host='localhost')

        def queue_get_for(context, *args):
            return 'network_topic'

        def kernel_ramdisk_mapping(*args, **kwargs):
            return (1, 1)

        def image_id_from_hash(*args, **kwargs):
            return 2

        self.stubs.Set(nova.db.api, 'project_get_networks',
                       project_get_networks)
        self.stubs.Set(nova.db.api, 'instance_create', instance_create)
        self.stubs.Set(nova.rpc, 'cast', fake_method)
        self.stubs.Set(nova.rpc, 'call', fake_method)
        self.stubs.Set(nova.db.api, 'instance_update',
            server_update)
        self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for)
        self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip',
            fake_method)
        self.stubs.Set(
            nova.api.openstack.create_instance_helper.CreateInstanceHelper,
            "_get_kernel_ramdisk_from_image", kernel_ramdisk_mapping)
        self.stubs.Set(nova.compute.api.API, "_find_host", find_host)

    def _test_create_instance_helper(self):
        self._setup_for_create_instance()

        body = dict(server=dict(
            name='server_test', imageId=3, flavorId=2,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"

        res = req.get_response(fakes.wsgi_app())

        self.assertEqual(res.status_int, 202)
        server = json.loads(res.body)['server']
        self.assertEqual(16, len(server['adminPass']))
        self.assertEqual('server_test', server['name'])
        self.assertEqual(1, server['id'])
        self.assertEqual(2, server['flavorId'])
        self.assertEqual(3, server['imageId'])
        self.assertEqual(FAKE_UUID, server['uuid'])

    def test_create_instance(self):
        self._test_create_instance_helper()

    def test_create_instance_has_uuid(self):
        """Tests at the db-layer instead of API layer since that's where the
           UUID is generated
        """
        ctxt = context.RequestContext(1, 1)
        values = {}
        instance = nova.db.api.instance_create(ctxt, values)
        expected = FAKE_UUID
        self.assertEqual(instance['uuid'], expected)

    def test_create_instance_via_zones(self):
        """Server generated ReservationID"""
        self._setup_for_create_instance()
        self.flags(allow_admin_api=True)

        body = dict(server=dict(
            name='server_test', imageId=3, flavorId=2,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.0/zones/boot')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"

        res = req.get_response(fakes.wsgi_app())

        reservation_id = json.loads(res.body)['reservation_id']
        self.assertEqual(res.status_int, 200)
        self.assertNotEqual(reservation_id, "")
        self.assertNotEqual(reservation_id, None)
        self.assertTrue(len(reservation_id) > 1)

    def test_create_instance_via_zones_with_resid(self):
        """User supplied ReservationID"""
        self._setup_for_create_instance()
        self.flags(allow_admin_api=True)

        body = dict(server=dict(
            name='server_test', imageId=3, flavorId=2,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}, reservation_id='myresid'))
        req = webob.Request.blank('/v1.0/zones/boot')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"

        res = req.get_response(fakes.wsgi_app())

        reservation_id = json.loads(res.body)['reservation_id']
        self.assertEqual(res.status_int, 200)
        self.assertEqual(reservation_id, "myresid")

    def test_create_instance_no_key_pair(self):
        fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False)
        self._test_create_instance_helper()

    def test_create_instance_no_name(self):
        self._setup_for_create_instance()

        body = {
            'server': {
                'imageId': 3,
                'flavorId': 1,
                'metadata': {
                    'hello': 'world',
                    'open': 'stack',
                },
                'personality': {},
            },
        }

        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_nonstring_name(self):
        self._setup_for_create_instance()

        body = {
            'server': {
                'name': 12,
                'imageId': 3,
                'flavorId': 1,
                'metadata': {
                    'hello': 'world',
                    'open': 'stack',
                },
                'personality': {},
            },
        }

        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_no_server_entity(self):
        self._setup_for_create_instance()

        body = {}

        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 422)

    def test_create_instance_whitespace_name(self):
        self._setup_for_create_instance()

        body = {
            'server': {
                'name': '    ',
                'imageId': 3,
                'flavorId': 1,
                'metadata': {
                    'hello': 'world',
                    'open': 'stack',
                },
                'personality': {},
            },
        }

        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_v1_1(self):
        self._setup_for_create_instance()

        # proper local hrefs must start with 'http://localhost/v1.1/'
        image_href = 'http://localhost/v1.1/images/2'
        flavor_ref = 'http://localhost/flavors/3'
        expected_flavor = {
            "id": "3",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/flavors/3',
                },
            ],
        }
        expected_image = {
            "id": "2",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/images/2',
                },
            ],
        }
        body = {
            'server': {
                'name': 'server_test',
                'imageRef': image_href,
                'flavorRef': flavor_ref,
                'metadata': {
                    'hello': 'world',
                    'open': 'stack',
                },
                'personality': [
                    {
                        "path": "/etc/banner.txt",
                        "contents": "MQ==",
                    },
                ],
            },
        }

        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"

        res = req.get_response(fakes.wsgi_app())

        self.assertEqual(res.status_int, 202)
        server = json.loads(res.body)['server']
        self.assertEqual(16, len(server['adminPass']))
        self.assertEqual(1, server['id'])
        self.assertEqual(0, server['progress'])
        self.assertEqual('server_test', server['name'])
        self.assertEqual(expected_flavor, server['flavor'])
        self.assertEqual(expected_image, server['image'])

    def test_create_instance_v1_1_invalid_flavor_href(self):
        self._setup_for_create_instance()

        image_href = 'http://localhost/v1.1/images/2'
        flavor_ref = 'http://localhost/v1.1/flavors/asdf'
        body = dict(server=dict(
            name='server_test', imageRef=image_href, flavorRef=flavor_ref,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_v1_1_invalid_flavor_id_int(self):
        self._setup_for_create_instance()

        image_href = 'http://localhost/v1.1/images/2'
        flavor_ref = -1
        body = dict(server=dict(
            name='server_test', imageRef=image_href, flavorRef=flavor_ref,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_v1_1_bad_flavor_href(self):
        self._setup_for_create_instance()

        image_href = 'http://localhost/v1.1/images/2'
        flavor_ref = 'http://localhost/v1.1/flavors/17'
        body = dict(server=dict(
            name='server_test', imageRef=image_href, flavorRef=flavor_ref,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_v1_1_bad_href(self):
        self._setup_for_create_instance()

        image_href = 'http://localhost/v1.1/images/asdf'
        flavor_ref = 'http://localhost/v1.1/flavors/3'
        body = dict(server=dict(
            name='server_test', imageRef=image_href, flavorRef=flavor_ref,
            metadata={'hello': 'world', 'open': 'stack'},
            personality={}))
        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_create_instance_v1_1_local_href(self):
        self._setup_for_create_instance()

        image_id = "2"
        flavor_ref = 'http://localhost/v1.1/flavors/3'
        expected_flavor = {
            "id": "3",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/flavors/3',
                },
            ],
        }
        expected_image = {
            "id": "2",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/images/2',
                },
            ],
        }
        body = {
            'server': {
                'name': 'server_test',
                'imageRef': image_id,
                'flavorRef': flavor_ref,
            },
        }

        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers["content-type"] = "application/json"

        res = req.get_response(fakes.wsgi_app())

        self.assertEqual(res.status_int, 202)
        server = json.loads(res.body)['server']
        self.assertEqual(expected_flavor, server['flavor'])
        self.assertEqual(expected_image, server['image'])

    def test_create_instance_with_admin_pass_v1_0(self):
        self._setup_for_create_instance()

        body = {
            'server': {
                'name': 'test-server-create',
                'imageId': 3,
                'flavorId': 1,
                'adminPass': 'testpass',
            },
        }

        req = webob.Request.blank('/v1.0/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers['content-type'] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        res = json.loads(res.body)
        self.assertNotEqual(res['server']['adminPass'],
                            body['server']['adminPass'])

    def test_create_instance_v1_1_admin_pass(self):
        self._setup_for_create_instance()

        image_href = 'http://localhost/v1.1/images/2'
        flavor_ref = 'http://localhost/v1.1/flavors/3'
        body = {
            'server': {
                'name': 'server_test',
                'imageRef': 3,
                'flavorRef': 3,
                'adminPass': 'testpass',
            },
        }

        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers['content-type'] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)
        server = json.loads(res.body)['server']
        self.assertEqual(server['adminPass'], body['server']['adminPass'])

    def test_create_instance_v1_1_admin_pass_empty(self):
        self._setup_for_create_instance()

        body = {
            'server': {
                'name': 'server_test',
                'imageRef': 3,
                'flavorRef': 3,
                'adminPass': '',
            },
        }

        req = webob.Request.blank('/v1.1/servers')
        req.method = 'POST'
        req.body = json.dumps(body)
        req.headers['content-type'] = "application/json"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_server_no_body(self):
        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_nonstring_name(self):
        """ Confirm that update is filtering params """
        inst_dict = dict(name=12, adminPass='bacon')
        self.body = json.dumps(dict(server=inst_dict))

        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        req.content_type = "application/json"
        req.body = self.body
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_whitespace_name(self):
        """ Confirm that update is filtering params """
        inst_dict = dict(name='   ', adminPass='bacon')
        self.body = json.dumps(dict(server=inst_dict))

        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        req.content_type = "application/json"
        req.body = self.body
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_null_name(self):
        """ Confirm that update is filtering params """
        inst_dict = dict(name='', adminPass='bacon')
        self.body = json.dumps(dict(server=inst_dict))

        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        req.content_type = "application/json"
        req.body = self.body
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_server_v1_0(self):
        inst_dict = dict(name='server_test', adminPass='bacon')
        self.body = json.dumps(dict(server=inst_dict))

        def server_update(context, id, params):
            filtered_dict = dict(display_name='server_test')
            self.assertEqual(params, filtered_dict)
            return filtered_dict

        self.stubs.Set(nova.db.api, 'instance_update',
            server_update)
        self.stubs.Set(nova.compute.api.API, "_find_host", find_host)
        mock_method = MockSetAdminPassword()
        self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method)

        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        req.content_type = "application/json"
        req.body = self.body
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 204)
        self.assertEqual(mock_method.instance_id, '1')
        self.assertEqual(mock_method.password, 'bacon')

    def test_update_server_no_body_v1_1(self):
        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'PUT'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 400)

    def test_update_server_name_v1_1(self):
        self.stubs.Set(nova.db.api, 'instance_get',
                return_server_with_attributes(name='server_test'))
        req = webob.Request.blank('/v1.1/servers/1')
        req.method = 'PUT'
        req.content_type = 'application/json'
        req.body = json.dumps({'server': {'name': 'server_test'}})
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server_test')

    def test_update_server_adminPass_ignored_v1_1(self):
        inst_dict = dict(name='server_test', adminPass='bacon')
        self.body = json.dumps(dict(server=inst_dict))

        def server_update(context, id, params):
            filtered_dict = dict(display_name='server_test')
            self.assertEqual(params, filtered_dict)
            return filtered_dict

        self.stubs.Set(nova.db.api, 'instance_update', server_update)
        self.stubs.Set(nova.db.api, 'instance_get',
                return_server_with_attributes(name='server_test'))

        req = webob.Request.blank('/v1.1/servers/1')
        req.method = 'PUT'
        req.content_type = "application/json"
        req.body = self.body
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['id'], 1)
        self.assertEqual(res_dict['server']['name'], 'server_test')

    def test_create_backup_schedules(self):
        req = webob.Request.blank('/v1.0/servers/1/backup_schedule')
        req.method = 'POST'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 501)

    def test_delete_backup_schedules(self):
        req = webob.Request.blank('/v1.0/servers/1/backup_schedule/1')
        req.method = 'DELETE'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 501)

    def test_get_server_backup_schedules(self):
        req = webob.Request.blank('/v1.0/servers/1/backup_schedule')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 501)

    def test_get_server_backup_schedule(self):
        req = webob.Request.blank('/v1.0/servers/1/backup_schedule/1')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 501)

    def test_server_backup_schedule_deprecated_v1_1(self):
        req = webob.Request.blank('/v1.1/servers/1/backup_schedule')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 404)

    def test_get_all_server_details_xml_v1_0(self):
        req = webob.Request.blank('/v1.0/servers/detail')
        req.headers['Accept'] = 'application/xml'
        res = req.get_response(fakes.wsgi_app())
        dom = minidom.parseString(res.body)
        for i, server in enumerate(dom.getElementsByTagName('server')):
            self.assertEqual(server.getAttribute('id'), str(i))
            self.assertEqual(server.getAttribute('hostId'), '')
            self.assertEqual(server.getAttribute('name'), 'server%d' % i)
            self.assertEqual(server.getAttribute('imageId'), '10')
            self.assertEqual(server.getAttribute('status'), 'BUILD')
            (meta,) = server.getElementsByTagName('meta')
            self.assertEqual(meta.getAttribute('key'), 'seq')
            self.assertEqual(meta.firstChild.data.strip(), str(i))

    def test_get_all_server_details_v1_0(self):
        req = webob.Request.blank('/v1.0/servers/detail')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)

        for i, s in enumerate(res_dict['servers']):
            self.assertEqual(s['id'], i)
            self.assertEqual(s['hostId'], '')
            self.assertEqual(s['name'], 'server%d' % i)
            self.assertEqual(s['imageId'], 10)
            self.assertEqual(s['flavorId'], 1)
            self.assertEqual(s['status'], 'BUILD')
            self.assertEqual(s['metadata']['seq'], str(i))

    def test_get_all_server_details_v1_1(self):
        expected_flavor = {
            "id": "1",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/flavors/1',
                },
            ],
        }
        expected_image = {
            "id": "10",
            "links": [
                {
                    "rel": "bookmark",
                    "href": 'http://localhost/images/10',
                },
            ],
        }
        req = webob.Request.blank('/v1.1/servers/detail')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        for i, s in enumerate(res_dict['servers']):
            self.assertEqual(s['id'], i)
            self.assertEqual(s['hostId'], '')
            self.assertEqual(s['name'], 'server%d' % i)
            self.assertEqual(s['image'], expected_image)
            self.assertEqual(s['flavor'], expected_flavor)
            self.assertEqual(s['status'], 'BUILD')
            self.assertEqual(s['metadata']['seq'], str(i))

    def test_get_all_server_details_with_host(self):
        '''
        We want to make sure that if two instances are on the same host, then
        they return the same hostId. If two instances are on different hosts,
        they should return different hostId's. In this test, there are 5
        instances - 2 on one host and 3 on another.
        '''

        def return_servers_with_host(context, *args, **kwargs):
            return [stub_instance(i, 'fake', 'fake', None, None, i % 2)
                    for i in xrange(5)]

        self.stubs.Set(nova.db.api, 'instance_get_all_by_filters',
            return_servers_with_host)

        req = webob.Request.blank('/v1.0/servers/detail')
        res = req.get_response(fakes.wsgi_app())
        res_dict = json.loads(res.body)

        server_list = res_dict['servers']
        host_ids = [server_list[0]['hostId'], server_list[1]['hostId']]
        self.assertTrue(host_ids[0] and host_ids[1])
        self.assertNotEqual(host_ids[0], host_ids[1])

        for i, s in enumerate(res_dict['servers']):
            self.assertEqual(s['id'], i)
            self.assertEqual(s['hostId'], host_ids[i % 2])
            self.assertEqual(s['name'], 'server%d' % i)
            self.assertEqual(s['imageId'], 10)
            self.assertEqual(s['flavorId'], 1)

    def test_server_pause(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank('/v1.0/servers/1/pause')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_unpause(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank('/v1.0/servers/1/unpause')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_suspend(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank('/v1.0/servers/1/suspend')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_resume(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank('/v1.0/servers/1/resume')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_reset_network(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank('/v1.0/servers/1/reset_network')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_inject_network_info(self):
        self.flags(allow_admin_api=True)
        req = webob.Request.blank(
              '/v1.0/servers/1/inject_network_info')
        req.method = 'POST'
        req.content_type = 'application/json'
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 202)

    def test_server_diagnostics(self):
        self.flags(allow_admin_api=False)
        req = webob.Request.blank("/v1.0/servers/1/diagnostics")
        req.method = "GET"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 404)

    def test_server_actions(self):
        self.flags(allow_admin_api=False)
        req = webob.Request.blank("/v1.0/servers/1/actions")
        req.method = "GET"
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 404)

    def test_delete_server_instance(self):
        req = webob.Request.blank('/v1.0/servers/1')
        req.method = 'DELETE'

        self.server_delete_called = False

        def instance_destroy_mock(context, id):
            self.server_delete_called = True

        self.stubs.Set(nova.db.api, 'instance_destroy',
            instance_destroy_mock)

        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status, '202 Accepted')
        self.assertEqual(self.server_delete_called, True)

    def test_rescue_accepted(self):
        self.flags(allow_admin_api=True)
        body = {}

        self.called = False

        def rescue_mock(*args, **kwargs):
            self.called = True

        self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock)
        req = webob.Request.blank('/v1.0/servers/1/rescue')
        req.method = 'POST'
        req.content_type = 'application/json'

        res = req.get_response(fakes.wsgi_app())

        self.assertEqual(self.called, True)
        self.assertEqual(res.status_int, 202)

    def test_rescue_raises_handled(self):
        self.flags(allow_admin_api=True)
        body = {}

        def rescue_mock(*args, **kwargs):
            raise Exception('Who cares?')

        self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock)
        req = webob.Request.blank('/v1.0/servers/1/rescue')
        req.method = 'POST'
        req.content_type = 'application/json'

        res = req.get_response(fakes.wsgi_app())

        self.assertEqual(res.status_int, 422)

    def test_delete_server_instance_v1_1(self):
        req = webob.Request.blank('/v1.1/servers/1')
        req.method = 'DELETE'

        self.server_delete_called = False

        def instance_destroy_mock(context, id):
            self.server_delete_called = True

        self.stubs.Set(nova.db.api, 'instance_destroy',
            instance_destroy_mock)

        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 204)
        self.assertEqual(self.server_delete_called, True)

    def test_shutdown_status(self):
        new_server = return_server_with_power_state(power_state.SHUTDOWN)
        self.stubs.Set(nova.db.api, 'instance_get', new_server)
        req = webob.Request.blank('/v1.0/servers/1')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['status'], 'SHUTDOWN')

    def test_shutoff_status(self):
        new_server = return_server_with_power_state(power_state.SHUTOFF)
        self.stubs.Set(nova.db.api, 'instance_get', new_server)
        req = webob.Request.blank('/v1.0/servers/1')
        res = req.get_response(fakes.wsgi_app())
        self.assertEqual(res.status_int, 200)
        res_dict = json.loads(res.body)
        self.assertEqual(res_dict['server']['status'], 'SHUTOFF')


class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase):

    def setUp(self):
        self.deserializer = create_instance_helper.ServerXMLDeserializer()

    def test_minimal_request(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1"/>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"server": {
                "name": "new-server-test",
                "imageId": "1",
                "flavorId": "1",
                "metadata": {},
                "personality": [],
                }}
        self.assertEquals(request['body'], expected)

    def test_request_with_empty_metadata(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata/>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"server": {
                "name": "new-server-test",
                "imageId": "1",
                "flavorId": "1",
                "metadata": {},
                "personality": [],
                }}
        self.assertEquals(request['body'], expected)

    def test_request_with_empty_personality(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <personality/>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"server": {
                "name": "new-server-test",
                "imageId": "1",
                "flavorId": "1",
                "metadata": {},
                "personality": [],
                }}
        self.assertEquals(request['body'], expected)

    def test_request_with_empty_metadata_and_personality(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata/>
    <personality/>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"server": {
                "name": "new-server-test",
                "imageId": "1",
                "flavorId": "1",
                "metadata": {},
                "personality": [],
                }}
        self.assertEquals(request['body'], expected)

    def test_request_with_empty_metadata_and_personality_reversed(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <personality/>
    <metadata/>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"server": {
                "name": "new-server-test",
                "imageId": "1",
                "flavorId": "1",
                "metadata": {},
                "personality": [],
                }}
        self.assertEquals(request['body'], expected)

    def test_request_with_one_personality(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <personality>
        <file path="/etc/conf">aabbccdd</file>
    </personality>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_with_two_personalities(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
<personality><file path="/etc/conf">aabbccdd</file>
<file path="/etc/sudoers">abcd</file></personality></server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"path": "/etc/conf", "contents": "aabbccdd"},
                    {"path": "/etc/sudoers", "contents": "abcd"}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_second_personality_node_ignored(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <personality>
        <file path="/etc/conf">aabbccdd</file>
    </personality>
    <personality>
        <file path="/etc/ignoreme">anything</file>
    </personality>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_with_one_personality_missing_path(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
<personality><file>aabbccdd</file></personality></server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"contents": "aabbccdd"}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_with_one_personality_empty_contents(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
<personality><file path="/etc/conf"></file></personality></server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"path": "/etc/conf", "contents": ""}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_with_one_personality_empty_contents_variation(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
<personality><file path="/etc/conf"/></personality></server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = [{"path": "/etc/conf", "contents": ""}]
        self.assertEquals(request['body']["server"]["personality"], expected)

    def test_request_with_one_metadata(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="alpha">beta</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"alpha": "beta"}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_two_metadata(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="alpha">beta</meta>
        <meta key="foo">bar</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"alpha": "beta", "foo": "bar"}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_metadata_missing_value(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="alpha"></meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"alpha": ""}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_two_metadata_missing_value(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="alpha"/>
        <meta key="delta"/>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"alpha": "", "delta": ""}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_metadata_missing_key(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta>beta</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"": "beta"}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_two_metadata_missing_key(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta>beta</meta>
        <meta>gamma</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"": "gamma"}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_request_with_metadata_duplicate_key(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="foo">bar</meta>
        <meta key="foo">baz</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {"foo": "baz"}
        self.assertEquals(request['body']["server"]["metadata"], expected)

    def test_canonical_request_from_docs(self):
        serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
 name="new-server-test" imageId="1" flavorId="1">
    <metadata>
        <meta key="My Server Name">Apache1</meta>
    </metadata>
    <personality>
        <file path="/etc/banner.txt">\
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp\
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k\
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs\
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g\
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo\
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==</file>
    </personality>
</server>"""
        expected = {"server": {
            "name": "new-server-test",
            "imageId": "1",
            "flavorId": "1",
            "metadata": {
                "My Server Name": "Apache1",
            },
            "personality": [
                {
                    "path": "/etc/banner.txt",
                    "contents": """\
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp\
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k\
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs\
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g\
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo\
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
                },
            ],
        }}
        request = self.deserializer.deserialize(serial_request, 'create')
        self.assertEqual(request['body'], expected)


class TestServerCreateRequestXMLDeserializerV11(test.TestCase):

    def setUp(self):
        super(TestServerCreateRequestXMLDeserializerV11, self).setUp()
        self.deserializer = create_instance_helper.ServerXMLDeserializerV11()

    def test_minimal_request(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="2"/>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "2",
            },
        }
        self.assertEquals(request['body'], expected)

    def test_admin_pass(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="2"
        adminPass="1234"/>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "2",
                "adminPass": "1234",
            },
        }
        self.assertEquals(request['body'], expected)

    def test_image_link(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="http://localhost:8774/v1.1/images/2"
        flavorRef="3"/>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "http://localhost:8774/v1.1/images/2",
                "flavorRef": "3",
            },
        }
        self.assertEquals(request['body'], expected)

    def test_flavor_link(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="http://localhost:8774/v1.1/flavors/3"/>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "http://localhost:8774/v1.1/flavors/3",
            },
        }
        self.assertEquals(request['body'], expected)

    def test_empty_metadata_personality(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="2">
    <metadata/>
    <personality/>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "2",
                "metadata": {},
                "personality": [],
            },
        }
        self.assertEquals(request['body'], expected)

    def test_multiple_metadata_items(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="2">
    <metadata>
        <meta key="one">two</meta>
        <meta key="open">snack</meta>
    </metadata>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "2",
                "metadata": {"one": "two", "open": "snack"},
            },
        }
        self.assertEquals(request['body'], expected)

    def test_multiple_personality_files(self):
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        name="new-server-test"
        imageRef="1"
        flavorRef="2">
    <personality>
        <file path="/etc/banner.txt">MQ==</file>
        <file path="/etc/hosts">Mg==</file>
    </personality>
</server>"""
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "1",
                "flavorRef": "2",
                "personality": [
                    {"path": "/etc/banner.txt", "contents": "MQ=="},
                    {"path": "/etc/hosts", "contents": "Mg=="},
                ],
            },
        }
        self.assertDictMatch(request['body'], expected)

    def test_spec_request(self):
        image_bookmark_link = "http://servers.api.openstack.org/1234/" + \
                              "images/52415800-8b69-11e0-9b19-734f6f006e54"
        serial_request = """
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
        imageRef="%s"
        flavorRef="52415800-8b69-11e0-9b19-734f1195ff37"
        name="new-server-test">
  <metadata>
    <meta key="My Server Name">Apache1</meta>
  </metadata>
  <personality>
    <file path="/etc/banner.txt">Mg==</file>
  </personality>
</server>""" % (image_bookmark_link)
        request = self.deserializer.deserialize(serial_request, 'create')
        expected = {
            "server": {
                "name": "new-server-test",
                "imageRef": "http://servers.api.openstack.org/1234/" + \
                            "images/52415800-8b69-11e0-9b19-734f6f006e54",
                "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37",
                "metadata": {"My Server Name": "Apache1"},
                "personality": [
                    {
                        "path": "/etc/banner.txt",
                        "contents": "Mg==",
                    },
                ],
            },
        }
        self.assertEquals(request['body'], expected)


class TestAddressesXMLSerialization(test.TestCase):

    serializer = nova.api.openstack.ips.IPXMLSerializer()

    def test_show(self):
        fixture = {
            'network_2': [
                {'addr': '192.168.0.1', 'version': 4},
                {'addr': 'fe80::beef', 'version': 6},
            ],
        }
        output = self.serializer.serialize(fixture, 'show')
        actual = minidom.parseString(output.replace("  ", ""))

        expected = minidom.parseString("""
            <network xmlns="http://docs.openstack.org/compute/api/v1.1"
                     id="network_2">
                <ip version="4" addr="192.168.0.1"/>
                <ip version="6" addr="fe80::beef"/>
            </network>
        """.replace("  ", ""))

        self.assertEqual(expected.toxml(), actual.toxml())

    def test_index(self):
        fixture = {
            'addresses': {
                'network_1': [
                    {'addr': '192.168.0.3', 'version': 4},
                    {'addr': '192.168.0.5', 'version': 4},
                ],
                'network_2': [
                    {'addr': '192.168.0.1', 'version': 4},
                    {'addr': 'fe80::beef', 'version': 6},
                ],
            },
        }
        output = self.serializer.serialize(fixture, 'index')
        actual = minidom.parseString(output.replace("  ", ""))

        expected = minidom.parseString("""
            <addresses xmlns="http://docs.openstack.org/compute/api/v1.1">
                <network id="network_2">
                    <ip version="4" addr="192.168.0.1"/>
                    <ip version="6" addr="fe80::beef"/>
                </network>
                <network id="network_1">
                    <ip version="4" addr="192.168.0.3"/>
                    <ip version="4" addr="192.168.0.5"/>
                </network>
            </addresses>
        """.replace("  ", ""))

        self.assertEqual(expected.toxml(), actual.toxml())


class TestServerInstanceCreation(test.TestCase):

    def setUp(self):
        super(TestServerInstanceCreation, self).setUp()
        fakes.stub_out_image_service(self.stubs)
        fakes.stub_out_key_pair_funcs(self.stubs)

    def _setup_mock_compute_api_for_personality(self):

        class MockComputeAPI(nova.compute.API):

            def __init__(self):
                self.injected_files = None

            def create(self, *args, **kwargs):
                if 'injected_files' in kwargs:
                    self.injected_files = kwargs['injected_files']
                else:
                    self.injected_files = None
                return [{'id': '1234', 'display_name': 'fakeinstance',
                         'uuid': FAKE_UUID}]

            def set_admin_password(self, *args, **kwargs):
                pass

        def make_stub_method(canned_return):
            def stub_method(*args, **kwargs):
                return canned_return
            return stub_method

        compute_api = MockComputeAPI()
        self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api))
        self.stubs.Set(
            nova.api.openstack.create_instance_helper.CreateInstanceHelper,
            '_get_kernel_ramdisk_from_image', make_stub_method((1, 1)))
        return compute_api

    def _create_personality_request_dict(self, personality_files):
        server = {}
        server['name'] = 'new-server-test'
        server['imageId'] = 1
        server['flavorId'] = 1
        if personality_files is not None:
            personalities = []
            for path, contents in personality_files:
                personalities.append({'path': path, 'contents': contents})
            server['personality'] = personalities
        return {'server': server}

    def _get_create_request_json(self, body_dict):
        req = webob.Request.blank('/v1.0/servers')
        req.headers['Content-Type'] = 'application/json'
        req.method = 'POST'
        req.body = json.dumps(body_dict)
        return req

    def _run_create_instance_with_mock_compute_api(self, request):
        compute_api = self._setup_mock_compute_api_for_personality()
        response = request.get_response(fakes.wsgi_app())
        return compute_api, response

    def _format_xml_request_body(self, body_dict):
        server = body_dict['server']
        body_parts = []
        body_parts.extend([
            '<?xml version="1.0" encoding="UTF-8"?>',
            '<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"',
            ' name="%s" imageId="%s" flavorId="%s">' % (
                    server['name'], server['imageId'], server['flavorId'])])
        if 'metadata' in server:
            metadata = server['metadata']
            body_parts.append('<metadata>')
            for item in metadata.iteritems():
                body_parts.append('<meta key="%s">%s</meta>' % item)
            body_parts.append('</metadata>')
        if 'personality' in server:
            personalities = server['personality']
            body_parts.append('<personality>')
            for file in personalities:
                item = (file['path'], file['contents'])
                body_parts.append('<file path="%s">%s</file>' % item)
            body_parts.append('</personality>')
        body_parts.append('</server>')
        return ''.join(body_parts)

    def _get_create_request_xml(self, body_dict):
        req = webob.Request.blank('/v1.0/servers')
        req.content_type = 'application/xml'
        req.accept = 'application/xml'
        req.method = 'POST'
        req.body = self._format_xml_request_body(body_dict)
        return req

    def _create_instance_with_personality_json(self, personality):
        body_dict = self._create_personality_request_dict(personality)
        request = self._get_create_request_json(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        return request, response, compute_api.injected_files

    def _create_instance_with_personality_xml(self, personality):
        body_dict = self._create_personality_request_dict(personality)
        request = self._get_create_request_xml(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        return request, response, compute_api.injected_files

    def test_create_instance_with_no_personality(self):
        request, response, injected_files = \
                self._create_instance_with_personality_json(personality=None)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, [])

    def test_create_instance_with_no_personality_xml(self):
        request, response, injected_files = \
                self._create_instance_with_personality_xml(personality=None)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, [])

    def test_create_instance_with_personality(self):
        path = '/my/file/path'
        contents = '#!/bin/bash\necho "Hello, World!"\n'
        b64contents = base64.b64encode(contents)
        personality = [(path, b64contents)]
        request, response, injected_files = \
            self._create_instance_with_personality_json(personality)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, [(path, contents)])

    def test_create_instance_with_personality_xml(self):
        path = '/my/file/path'
        contents = '#!/bin/bash\necho "Hello, World!"\n'
        b64contents = base64.b64encode(contents)
        personality = [(path, b64contents)]
        request, response, injected_files = \
            self._create_instance_with_personality_xml(personality)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, [(path, contents)])

    def test_create_instance_with_personality_no_path(self):
        personality = [('/remove/this/path',
            base64.b64encode('my\n\file\ncontents'))]
        body_dict = self._create_personality_request_dict(personality)
        del body_dict['server']['personality'][0]['path']
        request = self._get_create_request_json(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        self.assertEquals(response.status_int, 400)
        self.assertEquals(compute_api.injected_files, None)

    def _test_create_instance_with_personality_no_path_xml(self):
        personality = [('/remove/this/path',
            base64.b64encode('my\n\file\ncontents'))]
        body_dict = self._create_personality_request_dict(personality)
        request = self._get_create_request_xml(body_dict)
        request.body = request.body.replace(' path="/remove/this/path"', '')
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        self.assertEquals(response.status_int, 400)
        self.assertEquals(compute_api.injected_files, None)

    def test_create_instance_with_personality_no_contents(self):
        personality = [('/test/path',
            base64.b64encode('remove\nthese\ncontents'))]
        body_dict = self._create_personality_request_dict(personality)
        del body_dict['server']['personality'][0]['contents']
        request = self._get_create_request_json(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        self.assertEquals(response.status_int, 400)
        self.assertEquals(compute_api.injected_files, None)

    def test_create_instance_with_personality_not_a_list(self):
        personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
        body_dict = self._create_personality_request_dict(personality)
        body_dict['server']['personality'] = \
            body_dict['server']['personality'][0]
        request = self._get_create_request_json(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        self.assertEquals(response.status_int, 400)
        self.assertEquals(compute_api.injected_files, None)

    def test_create_instance_with_personality_with_non_b64_content(self):
        path = '/my/file/path'
        contents = '#!/bin/bash\necho "Oh no!"\n'
        personality = [(path, contents)]
        request, response, injected_files = \
            self._create_instance_with_personality_json(personality)
        self.assertEquals(response.status_int, 400)
        self.assertEquals(injected_files, None)

    def test_create_instance_with_null_personality(self):
        personality = None
        body_dict = self._create_personality_request_dict(personality)
        body_dict['server']['personality'] = None
        request = self._get_create_request_json(body_dict)
        compute_api, response = \
            self._run_create_instance_with_mock_compute_api(request)
        self.assertEquals(response.status_int, 202)

    def test_create_instance_with_three_personalities(self):
        files = [
            ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
            ('/etc/motd', 'Enjoy your root access!\n'),
            ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'),
            ]
        personality = []
        for path, content in files:
            personality.append((path, base64.b64encode(content)))
        request, response, injected_files = \
            self._create_instance_with_personality_json(personality)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, files)

    def test_create_instance_personality_empty_content(self):
        path = '/my/file/path'
        contents = ''
        personality = [(path, contents)]
        request, response, injected_files = \
            self._create_instance_with_personality_json(personality)
        self.assertEquals(response.status_int, 202)
        self.assertEquals(injected_files, [(path, contents)])

    def test_create_instance_admin_pass_json(self):
        request, response, dummy = \
            self._create_instance_with_personality_json(None)
        self.assertEquals(response.status_int, 202)
        response = json.loads(response.body)
        self.assertTrue('adminPass' in response['server'])
        self.assertEqual(16, len(response['server']['adminPass']))

    def test_create_instance_admin_pass_xml(self):
        request, response, dummy = \
            self._create_instance_with_personality_xml(None)
        self.assertEquals(response.status_int, 202)
        dom = minidom.parseString(response.body)
        server = dom.childNodes[0]
        self.assertEquals(server.nodeName, 'server')
        self.assertEqual(16, len(server.getAttribute('adminPass')))


class TestGetKernelRamdiskFromImage(test.TestCase):
    """
    If we're building from an AMI-style image, we need to be able to fetch the
    kernel and ramdisk associated with the machine image. This information is
    stored with the image metadata and return via the ImageService.

    These tests ensure that we parse the metadata return the ImageService
    correctly and that we handle failure modes appropriately.
    """

    def test_status_not_active(self):
        """We should only allow fetching of kernel and ramdisk information if
        we have a 'fully-formed' image, aka 'active'
        """
        image_meta = {'id': 1, 'status': 'queued'}
        self.assertRaises(exception.Invalid, self._get_k_r, image_meta)

    def test_not_ami(self):
        """Anything other than ami should return no kernel and no ramdisk"""
        image_meta = {'id': 1, 'status': 'active', 'container_format': 'vhd'}
        kernel_id, ramdisk_id = self._get_k_r(image_meta)
        self.assertEqual(kernel_id, None)
        self.assertEqual(ramdisk_id, None)

    def test_ami_no_kernel(self):
        """If an ami is missing a kernel it should raise NotFound"""
        image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami',
                      'properties': {'ramdisk_id': 1}}
        self.assertRaises(exception.NotFound, self._get_k_r, image_meta)

    def test_ami_no_ramdisk(self):
        """If an ami is missing a ramdisk it should raise NotFound"""
        image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami',
                      'properties': {'kernel_id': 1}}
        self.assertRaises(exception.NotFound, self._get_k_r, image_meta)

    def test_ami_kernel_ramdisk_present(self):
        """Return IDs if both kernel and ramdisk are present"""
        image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami',
                      'properties': {'kernel_id': 1, 'ramdisk_id': 2}}
        kernel_id, ramdisk_id = self._get_k_r(image_meta)
        self.assertEqual(kernel_id, 1)
        self.assertEqual(ramdisk_id, 2)

    @staticmethod
    def _get_k_r(image_meta):
        """Rebinding function to a shorter name for convenience"""
        kernel_id, ramdisk_id = create_instance_helper.CreateInstanceHelper. \
                                _do_get_kernel_ramdisk_from_image(image_meta)
        return kernel_id, ramdisk_id


class ServersViewBuilderV11Test(test.TestCase):

    def setUp(self):
        self.instance = self._get_instance()
        self.view_builder = self._get_view_builder()

    def tearDown(self):
        pass

    def _get_instance(self):
        created_at = datetime.datetime(2010, 10, 10, 12, 0, 0)
        updated_at = datetime.datetime(2010, 11, 11, 11, 0, 0)
        instance = {
            "id": 1,
            "created_at": created_at,
            "updated_at": updated_at,
            "admin_pass": "",
            "user_id": "",
            "project_id": "",
            "image_ref": "5",
            "kernel_id": "",
            "ramdisk_id": "",
            "launch_index": 0,
            "key_name": "",
            "key_data": "",
            "state": 0,
            "state_description": "",
            "memory_mb": 0,
            "vcpus": 0,
            "local_gb": 0,
            "hostname": "",
            "host": "",
            "instance_type": {
               "flavorid": 1,
            },
            "user_data": "",
            "reservation_id": "",
            "mac_address": "",
            "scheduled_at": utils.utcnow(),
            "launched_at": utils.utcnow(),
            "terminated_at": utils.utcnow(),
            "availability_zone": "",
            "display_name": "test_server",
            "display_description": "",
            "locked": False,
            "metadata": [],
            #"address": ,
            #"floating_ips": [{"address":ip} for ip in public_addresses]}
            "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"}

        return instance

    def _get_view_builder(self):
        base_url = "http://localhost/v1.1"
        views = nova.api.openstack.views
        address_builder = views.addresses.ViewBuilderV11()
        flavor_builder = views.flavors.ViewBuilderV11(base_url)
        image_builder = views.images.ViewBuilderV11(base_url)

        view_builder = nova.api.openstack.views.servers.ViewBuilderV11(
            address_builder,
            flavor_builder,
            image_builder,
            base_url,
            )
        return view_builder

    def test_build_server(self):
        expected_server = {
            "server": {
                "id": 1,
                "uuid": self.instance['uuid'],
                "name": "test_server",
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        output = self.view_builder.build(self.instance, False)
        self.assertDictMatch(output, expected_server)

    def test_build_server_detail(self):
        image_bookmark = "http://localhost/images/5"
        flavor_bookmark = "http://localhost/flavors/1"
        expected_server = {
            "server": {
                "id": 1,
                "uuid": self.instance['uuid'],
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 0,
                "name": "test_server",
                "status": "BUILD",
                "hostId": '',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                  "links": [
                                            {
                          "rel": "bookmark",
                          "href": flavor_bookmark,
                      },
                  ],
                },
                "addresses": {},
                "metadata": {},
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        output = self.view_builder.build(self.instance, True)
        self.assertDictMatch(output, expected_server)

    def test_build_server_detail_active_status(self):
        #set the power state of the instance to running
        self.instance['state'] = 1
        image_bookmark = "http://localhost/images/5"
        flavor_bookmark = "http://localhost/flavors/1"
        expected_server = {
            "server": {
                "id": 1,
                "uuid": self.instance['uuid'],
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 100,
                "name": "test_server",
                "status": "ACTIVE",
                "hostId": '',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                  "links": [
                                            {
                          "rel": "bookmark",
                          "href": flavor_bookmark,
                      },
                  ],
                },
                "addresses": {},
                "metadata": {},
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        output = self.view_builder.build(self.instance, True)
        self.assertDictMatch(output, expected_server)

    def test_build_server_detail_with_metadata(self):

        metadata = []
        metadata.append(InstanceMetadata(key="Open", value="Stack"))
        metadata.append(InstanceMetadata(key="Number", value=1))
        self.instance['metadata'] = metadata

        image_bookmark = "http://localhost/images/5"
        flavor_bookmark = "http://localhost/flavors/1"
        expected_server = {
            "server": {
                "id": 1,
                "uuid": self.instance['uuid'],
                "updated": "2010-11-11T11:00:00Z",
                "created": "2010-10-10T12:00:00Z",
                "progress": 0,
                "name": "test_server",
                "status": "BUILD",
                "hostId": '',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                    "links": [
                                              {
                            "rel": "bookmark",
                            "href": flavor_bookmark,
                        },
                    ],
                },
                "addresses": {},
                "metadata": {
                    "Open": "Stack",
                    "Number": "1",
                },
                "links": [
                    {
                        "rel": "self",
                        "href": "http://localhost/v1.1/servers/1",
                    },
                    {
                        "rel": "bookmark",
                        "href": "http://localhost/servers/1",
                    },
                ],
            }
        }

        output = self.view_builder.build(self.instance, True)
        self.assertDictMatch(output, expected_server)


class ServerXMLSerializationTest(test.TestCase):

    TIMESTAMP = "2010-10-11T10:30:22Z"
    SERVER_HREF = 'http://localhost/v1.1/servers/123'
    SERVER_BOOKMARK = 'http://localhost/servers/123'
    IMAGE_BOOKMARK = 'http://localhost/images/5'
    FLAVOR_BOOKMARK = 'http://localhost/flavors/1'

    def setUp(self):
        self.maxDiff = None
        test.TestCase.setUp(self)

    def test_show(self):
        serializer = servers.ServerXMLSerializer()

        fixture = {
            "server": {
                "id": 1,
                "uuid": FAKE_UUID,
                'created': self.TIMESTAMP,
                'updated': self.TIMESTAMP,
                "progress": 0,
                "name": "test_server",
                "status": "BUILD",
                "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": self.IMAGE_BOOKMARK,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": self.FLAVOR_BOOKMARK,
                        },
                    ],
                },
                "addresses": {
                    "network_one": [
                        {
                            "version": 4,
                            "addr": "67.23.10.138",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.138",
                        },
                    ],
                    "network_two": [
                        {
                            "version": 4,
                            "addr": "67.23.10.139",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.139",
                        },
                    ],
                },
                "metadata": {
                    "Open": "Stack",
                    "Number": "1",
                },
                'links': [
                    {
                        'href': self.SERVER_HREF,
                        'rel': 'self',
                    },
                    {
                        'href': self.SERVER_BOOKMARK,
                        'rel': 'bookmark',
                    },
                ],
            }
        }

        output = serializer.serialize(fixture, 'show')
        actual = minidom.parseString(output.replace("  ", ""))

        expected_server_href = self.SERVER_HREF
        expected_server_bookmark = self.SERVER_BOOKMARK
        expected_image_bookmark = self.IMAGE_BOOKMARK
        expected_flavor_bookmark = self.FLAVOR_BOOKMARK
        expected_now = self.TIMESTAMP
        expected_uuid = FAKE_UUID
        expected = minidom.parseString("""
        <server id="1"
                uuid="%(expected_uuid)s"
                xmlns="http://docs.openstack.org/compute/api/v1.1"
                xmlns:atom="http://www.w3.org/2005/Atom"
                name="test_server"
                updated="%(expected_now)s"
                created="%(expected_now)s"
                hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
                status="BUILD"
                progress="0">
            <atom:link href="%(expected_server_href)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
            <image id="5">
                <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
            </image>
            <flavor id="1">
                <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
            </flavor>
            <metadata>
                <meta key="Open">
                    Stack
                </meta>
                <meta key="Number">
                    1
                </meta>
            </metadata>
            <addresses>
                <network id="network_one">
                    <ip version="4" addr="67.23.10.138"/>
                    <ip version="6" addr="::babe:67.23.10.138"/>
                </network>
                <network id="network_two">
                    <ip version="4" addr="67.23.10.139"/>
                    <ip version="6" addr="::babe:67.23.10.139"/>
                </network>
            </addresses>
        </server>
        """.replace("  ", "") % (locals()))

        self.assertEqual(expected.toxml(), actual.toxml())

    def test_create(self):
        serializer = servers.ServerXMLSerializer()

        fixture = {
            "server": {
                "id": 1,
                "uuid": FAKE_UUID,
                'created': self.TIMESTAMP,
                'updated': self.TIMESTAMP,
                "progress": 0,
                "name": "test_server",
                "status": "BUILD",
                "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
                "adminPass": "test_password",
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": self.IMAGE_BOOKMARK,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": self.FLAVOR_BOOKMARK,
                        },
                    ],
                },
                "addresses": {
                    "network_one": [
                        {
                            "version": 4,
                            "addr": "67.23.10.138",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.138",
                        },
                    ],
                    "network_two": [
                        {
                            "version": 4,
                            "addr": "67.23.10.139",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.139",
                        },
                    ],
                },
                "metadata": {
                    "Open": "Stack",
                    "Number": "1",
                },
                'links': [
                    {
                        'href': self.SERVER_HREF,
                        'rel': 'self',
                    },
                    {
                        'href': self.SERVER_BOOKMARK,
                        'rel': 'bookmark',
                    },
                ],
            }
        }

        output = serializer.serialize(fixture, 'create')
        actual = minidom.parseString(output.replace("  ", ""))

        expected_server_href = self.SERVER_HREF
        expected_server_bookmark = self.SERVER_BOOKMARK
        expected_image_bookmark = self.IMAGE_BOOKMARK
        expected_flavor_bookmark = self.FLAVOR_BOOKMARK
        expected_now = self.TIMESTAMP
        expected_uuid = FAKE_UUID
        expected = minidom.parseString("""
        <server id="1"
                uuid="%(expected_uuid)s"
                xmlns="http://docs.openstack.org/compute/api/v1.1"
                xmlns:atom="http://www.w3.org/2005/Atom"
                name="test_server"
                updated="%(expected_now)s"
                created="%(expected_now)s"
                hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
                status="BUILD"
                adminPass="test_password"
                progress="0">
            <atom:link href="%(expected_server_href)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
            <image id="5">
                <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
            </image>
            <flavor id="1">
                <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
            </flavor>
            <metadata>
                <meta key="Open">
                    Stack
                </meta>
                <meta key="Number">
                    1
                </meta>
            </metadata>
            <addresses>
                <network id="network_one">
                    <ip version="4" addr="67.23.10.138"/>
                    <ip version="6" addr="::babe:67.23.10.138"/>
                </network>
                <network id="network_two">
                    <ip version="4" addr="67.23.10.139"/>
                    <ip version="6" addr="::babe:67.23.10.139"/>
                </network>
            </addresses>
        </server>
        """.replace("  ", "") % (locals()))

        self.assertEqual(expected.toxml(), actual.toxml())

    def test_index(self):
        serializer = servers.ServerXMLSerializer()

        expected_server_href = 'http://localhost/v1.1/servers/1'
        expected_server_bookmark = 'http://localhost/servers/1'
        expected_server_href_2 = 'http://localhost/v1.1/servers/2'
        expected_server_bookmark_2 = 'http://localhost/servers/2'
        fixture = {"servers": [
            {
                "id": 1,
                "name": "test_server",
                'links': [
                    {
                        'href': expected_server_href,
                        'rel': 'self',
                    },
                    {
                        'href': expected_server_bookmark,
                        'rel': 'bookmark',
                    },
                ],
            },
            {
                "id": 2,
                "name": "test_server_2",
                'links': [
                    {
                        'href': expected_server_href_2,
                        'rel': 'self',
                    },
                    {
                        'href': expected_server_bookmark_2,
                        'rel': 'bookmark',
                    },
                ],
            },
        ]}

        output = serializer.serialize(fixture, 'index')
        actual = minidom.parseString(output.replace("  ", ""))

        expected = minidom.parseString("""
        <servers xmlns="http://docs.openstack.org/compute/api/v1.1"
                 xmlns:atom="http://www.w3.org/2005/Atom">
        <server id="1" name="test_server">
            <atom:link href="%(expected_server_href)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
        </server>
        <server id="2" name="test_server_2">
            <atom:link href="%(expected_server_href_2)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/>
        </server>
        </servers>
        """.replace("  ", "") % (locals()))

        self.assertEqual(expected.toxml(), actual.toxml())

    def test_detail(self):
        serializer = servers.ServerXMLSerializer()

        expected_server_href = 'http://localhost/v1.1/servers/1'
        expected_server_bookmark = 'http://localhost/servers/1'
        expected_image_bookmark = self.IMAGE_BOOKMARK
        expected_flavor_bookmark = self.FLAVOR_BOOKMARK
        expected_now = self.TIMESTAMP
        expected_uuid = FAKE_UUID

        expected_server_href_2 = 'http://localhost/v1.1/servers/2'
        expected_server_bookmark_2 = 'http://localhost/servers/2'
        fixture = {"servers": [
            {
                "id": 1,
                "uuid": FAKE_UUID,
                'created': self.TIMESTAMP,
                'updated': self.TIMESTAMP,
                "progress": 0,
                "name": "test_server",
                "status": "BUILD",
                "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": expected_image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": expected_flavor_bookmark,
                        },
                    ],
                },
                "addresses": {
                    "network_one": [
                        {
                            "version": 4,
                            "addr": "67.23.10.138",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.138",
                        },
                    ],
                },
                "metadata": {
                    "Number": "1",
                },
                "links": [
                    {
                        "href": expected_server_href,
                        "rel": "self",
                    },
                    {
                        "href": expected_server_bookmark,
                        "rel": "bookmark",
                    },
                ],
            },
            {
                "id": 2,
                "uuid": FAKE_UUID,
                'created': self.TIMESTAMP,
                'updated': self.TIMESTAMP,
                "progress": 100,
                "name": "test_server_2",
                "status": "ACTIVE",
                "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0',
                "image": {
                    "id": "5",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": expected_image_bookmark,
                        },
                    ],
                },
                "flavor": {
                    "id": "1",
                    "links": [
                        {
                            "rel": "bookmark",
                            "href": expected_flavor_bookmark,
                        },
                    ],
                },
                "addresses": {
                    "network_one": [
                        {
                            "version": 4,
                            "addr": "67.23.10.138",
                        },
                        {
                            "version": 6,
                            "addr": "::babe:67.23.10.138",
                        },
                    ],
                },
                "metadata": {
                    "Number": "2",
                },
                "links": [
                    {
                        "href": expected_server_href_2,
                        "rel": "self",
                    },
                    {
                        "href": expected_server_bookmark_2,
                        "rel": "bookmark",
                    },
                ],
            },
        ]}

        output = serializer.serialize(fixture, 'detail')
        actual = minidom.parseString(output.replace("  ", ""))

        expected = minidom.parseString("""
        <servers xmlns="http://docs.openstack.org/compute/api/v1.1"
                 xmlns:atom="http://www.w3.org/2005/Atom">
        <server id="1"
                uuid="%(expected_uuid)s"
                name="test_server"
                updated="%(expected_now)s"
                created="%(expected_now)s"
                hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
                status="BUILD"
                progress="0">
            <atom:link href="%(expected_server_href)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/>
            <image id="5">
                <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
            </image>
            <flavor id="1">
                <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
            </flavor>
            <metadata>
                <meta key="Number">
                    1
                </meta>
            </metadata>
            <addresses>
                <network id="network_one">
                    <ip version="4" addr="67.23.10.138"/>
                    <ip version="6" addr="::babe:67.23.10.138"/>
                </network>
            </addresses>
        </server>
        <server id="2"
                uuid="%(expected_uuid)s"
                name="test_server_2"
                updated="%(expected_now)s"
                created="%(expected_now)s"
                hostId="e4d909c290d0fb1ca068ffaddf22cbd0"
                status="ACTIVE"
                progress="100">
            <atom:link href="%(expected_server_href_2)s" rel="self"/>
            <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/>
            <image id="5">
                <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/>
            </image>
            <flavor id="1">
                <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/>
            </flavor>
            <metadata>
                <meta key="Number">
                    2
                </meta>
            </metadata>
            <addresses>
                <network id="network_one">
                    <ip version="4" addr="67.23.10.138"/>
                    <ip version="6" addr="::babe:67.23.10.138"/>
                </network>
            </addresses>
        </server>
        </servers>
        """.replace("  ", "") % (locals()))

        self.assertEqual(expected.toxml(), actual.toxml())
