Pass tempest list_server_filters test

Change the following to pass tempest list_server_filters test:

(1) Change response body of Nova API gateway image controller
(2) Add network controller
(3) Add server list filters support
(4) Change response body of server list

Change-Id: I96013e2a3c871640b530611e36fa0a219c5e0516
This commit is contained in:
zhiyuan_cai 2016-04-20 16:44:40 +08:00
parent 353ebcddb1
commit e43c4bc656
8 changed files with 342 additions and 42 deletions

View File

@ -30,10 +30,11 @@ NONE_DONE = 2 # neither router nor bottom resources exists
def get_or_create_route(t_ctx, q_ctx,
project_id, pod, _id, _type, list_ele_method):
project_id, pod, ele, _type, list_ele_method):
# use configuration option later
route_expire_threshold = 30
_id = ele['id']
with t_ctx.session.begin():
routes = core.query_resource(
t_ctx, models.ResourceRouting,
@ -53,7 +54,7 @@ def get_or_create_route(t_ctx, q_ctx,
# a race here that other worker is updating this route, we
# need to check if the corresponding element has been
# created by other worker
eles = list_ele_method(t_ctx, q_ctx, pod, _id, _type)
eles = list_ele_method(t_ctx, q_ctx, pod, ele, _type)
if eles:
route['bottom_id'] = eles[0]['id']
core.update_resource(t_ctx,
@ -92,7 +93,7 @@ def get_or_create_element(t_ctx, q_ctx,
max_tries = 5
for _ in xrange(max_tries):
route, status = get_or_create_route(
t_ctx, q_ctx, project_id, pod, ele['id'], _type, list_ele_method)
t_ctx, q_ctx, project_id, pod, ele, _type, list_ele_method)
if not route:
eventlet.sleep(0)
continue
@ -101,7 +102,7 @@ def get_or_create_element(t_ctx, q_ctx,
break
if status == NONE_DONE:
try:
ele = create_ele_method(t_ctx, q_ctx, pod, body, _type)
new_ele = create_ele_method(t_ctx, q_ctx, pod, body, _type)
except Exception:
with t_ctx.session.begin():
try:
@ -118,7 +119,7 @@ def get_or_create_element(t_ctx, q_ctx,
# NOTE(zhiyuan) it's safe to update route, the bottom network
# has been successfully created, so other worker will not
# delete this route
route['bottom_id'] = ele['id']
route['bottom_id'] = new_ele['id']
core.update_resource(t_ctx, models.ResourceRouting,
route['id'], route)
break

View File

@ -112,3 +112,7 @@ def check_string_length(value, name=None, min_len=0, max_len=None):
msg = _("%(name)s has more than %(max_length)s "
"characters.") % {'name': name, 'max_length': max_len}
raise t_exceptions.InvalidInput(message=msg)
def get_bottom_network_name(network):
return '%s#%s' % (network['id'], network['name'])

View File

@ -49,6 +49,7 @@ import tricircle.common.exceptions as t_exceptions
from tricircle.common.i18n import _
from tricircle.common.i18n import _LI
import tricircle.common.lock_handle as t_lock
from tricircle.common import utils
from tricircle.common import xrpcapi
import tricircle.db.api as db_api
from tricircle.db import core
@ -629,9 +630,9 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def _prepare_top_element(self, t_ctx, q_ctx,
project_id, pod, ele, _type, body):
def list_resources(t_ctx_, q_ctx_, pod_, _id_, _type_):
def list_resources(t_ctx_, q_ctx_, pod_, ele_, _type_):
return getattr(self, 'get_%ss' % _type_)(
q_ctx_, filters={'name': _id_})
q_ctx_, filters={'name': ele_['id']})
def create_resources(t_ctx_, q_ctx_, pod_, body_, _type_):
return getattr(self, 'create_%s' % _type_)(q_ctx_, body_)
@ -643,11 +644,15 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def _prepare_bottom_element(self, t_ctx,
project_id, pod, ele, _type, body):
def list_resources(t_ctx_, q_ctx, pod_, _id_, _type_):
def list_resources(t_ctx_, q_ctx, pod_, ele_, _type_):
client = self._get_client(pod_['pod_name'])
return client.list_resources(_type_, t_ctx_, [{'key': 'name',
'comparator': 'eq',
'value': _id_}])
if _type_ == t_constants.RT_NETWORK:
value = utils.get_bottom_network_name(ele_)
else:
value = ele_['id']
return client.list_resources(_type_, t_ctx_,
[{'key': 'name', 'comparator': 'eq',
'value': value}])
def create_resources(t_ctx_, q_ctx, pod_, body_, _type_):
client = self._get_client(pod_['pod_name'])
@ -753,7 +758,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
net_body = {
'network': {
'tenant_id': project_id,
'name': t_net['id'],
'name': utils.get_bottom_network_name(t_net),
'admin_state_up': True
}
}

View File

@ -16,9 +16,52 @@
import pecan
from pecan import expose
from pecan import rest
import re
import urlparse
import tricircle.common.client as t_client
from tricircle.common import constants
import tricircle.common.context as t_context
import tricircle.db.api as db_api
def url_join(*parts):
"""Convenience method for joining parts of a URL
Any leading and trailing '/' characters are removed, and the parts joined
together with '/' as a separator. If last element of 'parts' is an empty
string, the returned URL will have a trailing slash.
"""
parts = parts or ['']
clean_parts = [part.strip('/') for part in parts if part]
if not parts[-1]:
# Empty last element should add a trailing slash
clean_parts.append('')
return '/'.join(clean_parts)
def remove_trailing_version_from_href(href):
"""Removes the api version from the href.
Given: 'http://www.nova.com/compute/v1.1'
Returns: 'http://www.nova.com/compute'
Given: 'http://www.nova.com/v1.1'
Returns: 'http://www.nova.com'
"""
parsed_url = urlparse.urlsplit(href)
url_parts = parsed_url.path.rsplit('/', 1)
# NOTE: this should match vX.X or vX
expression = re.compile(r'^v([0-9]+|[0-9]+\.[0-9]+)(/.*|$)')
if not expression.match(url_parts.pop()):
raise ValueError('URL %s does not contain version' % href)
new_path = url_join(*url_parts)
parsed_url = list(parsed_url)
parsed_url[2] = new_path
return urlparse.urlunsplit(parsed_url)
class ImageController(rest.RestController):
@ -27,6 +70,72 @@ class ImageController(rest.RestController):
self.project_id = project_id
self.client = t_client.Client()
def _get_links(self, context, image):
nova_url = self.client.get_endpoint(
context, db_api.get_top_pod(context)['pod_id'],
constants.ST_NOVA)
nova_url = nova_url.replace('/$(tenant_id)s', '')
self_link = url_join(nova_url, self.project_id, 'images', image['id'])
bookmark_link = url_join(
remove_trailing_version_from_href(nova_url),
self.project_id, 'images', image['id'])
glance_url = self.client.get_endpoint(
context, db_api.get_top_pod(context)['pod_id'],
constants.ST_GLANCE)
alternate_link = '/'.join([glance_url, 'images', image['id']])
return [{'rel': 'self', 'href': self_link},
{'rel': 'bookmark', 'href': bookmark_link},
{'rel': 'alternate',
'type': 'application/vnd.openstack.image',
'href': alternate_link}]
@staticmethod
def _format_date(dt):
"""Return standard format for a given datetime string."""
if dt is not None:
date_string = dt.split('.')[0]
date_string += 'Z'
return date_string
@staticmethod
def _get_status(image):
"""Update the status field to standardize format."""
return {
'active': 'ACTIVE',
'queued': 'SAVING',
'saving': 'SAVING',
'deleted': 'DELETED',
'pending_delete': 'DELETED',
'killed': 'ERROR',
}.get(image.get('status'), 'UNKNOWN')
@staticmethod
def _get_progress(image):
return {
'queued': 25,
'saving': 50,
'active': 100,
}.get(image.get('status'), 0)
def _construct_list_image_entry(self, context, image):
return {'id': image['id'],
'name': image.get('name'),
'links': self._get_links(context, image)}
def _construct_show_image_entry(self, context, image):
return {
'id': image['id'],
'name': image.get('name'),
'minRam': int(image.get('min_ram') or 0),
'minDisk': int(image.get('min_disk') or 0),
'metadata': image.get('properties', {}),
'created': self._format_date(image.get('created_at')),
'updated': self._format_date(image.get('updated_at')),
'status': self._get_status(image),
'progress': self._get_progress(image),
'links': self._get_links(context, image)
}
@expose(generic=True, template='json')
def get_one(self, _id):
context = t_context.extract_context_from_environ()
@ -34,10 +143,12 @@ class ImageController(rest.RestController):
if not image:
pecan.abort(404, 'Image not found')
return
return {'image': image}
return {'image': self._construct_show_image_entry(context, image)}
@expose(generic=True, template='json')
def get_all(self):
context = t_context.extract_context_from_environ()
images = self.client.list_images(context)
return {'images': images}
ret_images = [self._construct_list_image_entry(
context, image) for image in images]
return {'images': ret_images}

View File

@ -0,0 +1,50 @@
# Copyright (c) 2015 Huawei Tech. Co., Ltd.
# 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 pecan
from pecan import expose
from pecan import rest
import tricircle.common.client as t_client
import tricircle.common.context as t_context
class NetworkController(rest.RestController):
def __init__(self, project_id):
self.project_id = project_id
self.client = t_client.Client()
@staticmethod
def _construct_network_entry(network):
network['uuid'] = network['id']
network['label'] = network['name']
return network
@expose(generic=True, template='json')
def get_one(self, _id):
context = t_context.extract_context_from_environ()
network = self.client.get_networks(context, _id)
if not network:
pecan.abort(404, 'Network not found')
return
return {'network': self._construct_network_entry(network)}
@expose(generic=True, template='json')
def get_all(self):
context = t_context.extract_context_from_environ()
networks = self.client.list_networks(context)
return {'networks': [self._construct_network_entry(
network) for network in networks]}

View File

@ -29,6 +29,7 @@ from tricircle.nova_apigw.controllers import action
from tricircle.nova_apigw.controllers import aggregate
from tricircle.nova_apigw.controllers import flavor
from tricircle.nova_apigw.controllers import image
from tricircle.nova_apigw.controllers import network
from tricircle.nova_apigw.controllers import quota_sets
from tricircle.nova_apigw.controllers import server
from tricircle.nova_apigw.controllers import volume
@ -94,6 +95,7 @@ class V21Controller(object):
'images': image.ImageController,
'os-quota-sets': quota_sets.QuotaSetsController,
'limits': quota_sets.LimitsController,
'os-networks': network.NetworkController
}
self.server_sub_controller = {
'os-volume_attachments': volume.VolumeController,

View File

@ -56,24 +56,45 @@ class ServerController(rest.RestController):
self.clients[pod_name] = t_client.Client(pod_name)
return self.clients[pod_name]
def _get_all(self, context):
def _get_all(self, context, params):
filters = [{'key': key,
'comparator': 'eq',
'value': value} for key, value in params.iteritems()]
ret = []
pods = db_api.list_pods(context)
for pod in pods:
if not pod['az_name']:
continue
client = self._get_client(pod['pod_name'])
servers = client.list_servers(context)
servers = client.list_servers(context, filters=filters)
self._remove_fip_info(servers)
ret.extend(servers)
return ret
@staticmethod
def _construct_brief_server_entry(server):
return {'id': server['id'],
'name': server.get('name'),
'links': server.get('links')}
@staticmethod
def _transform_network_name(server):
if 'addresses' not in server:
return
keys = [key for key in server['addresses'].iterkeys()]
for key in keys:
value = server['addresses'].pop(key)
network_name = key.split('#')[1]
server['addresses'][network_name] = value
return server
@expose(generic=True, template='json')
def get_one(self, _id):
def get_one(self, _id, **kwargs):
context = t_context.extract_context_from_environ()
if _id == 'detail':
return {'servers': self._get_all(context)}
return {'servers': [self._transform_network_name(
server) for server in self._get_all(context, kwargs)]}
mappings = db_api.get_bottom_mappings_by_top_id(
context, _id, constants.RT_SERVER)
@ -88,12 +109,14 @@ class ServerController(rest.RestController):
pecan.abort(404, 'Server not found')
return
else:
self._transform_network_name(server)
return {'server': server}
@expose(generic=True, template='json')
def get_all(self):
def get_all(self, **kwargs):
context = t_context.extract_context_from_environ()
return {'servers': self._get_all(context)}
return {'servers': [self._construct_brief_server_entry(
server) for server in self._get_all(context, kwargs)]}
@expose(generic=True, template='json')
def post(self, **kw):
@ -216,6 +239,7 @@ class ServerController(rest.RestController):
'pod_id': pod['pod_id'],
'project_id': self.project_id,
'resource_type': constants.RT_SERVER})
pecan.response.status = 202
return {'server': server}
@expose(generic=True, template='json')
@ -265,21 +289,21 @@ class ServerController(rest.RestController):
return pecan.response
def _get_or_create_route(self, context, pod, _id, _type):
def list_resources(t_ctx, q_ctx, pod_, _id_, _type_):
def list_resources(t_ctx, q_ctx, pod_, ele, _type_):
client = self._get_client(pod_['pod_name'])
return client.list_resources(_type_, t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': _id_}])
'value': ele['id']}])
return t_lock.get_or_create_route(context, None,
self.project_id, pod, _id, _type,
list_resources)
self.project_id, pod, {'id': _id},
_type, list_resources)
def _get_create_network_body(self, network):
body = {
'network': {
'tenant_id': self.project_id,
'name': network['id'],
'name': utils.get_bottom_network_name(network),
'admin_state_up': True
}
}
@ -342,11 +366,16 @@ class ServerController(rest.RestController):
return body
def _prepare_neutron_element(self, context, pod, ele, _type, body):
def list_resources(t_ctx, q_ctx, pod_, _id_, _type_):
def list_resources(t_ctx, q_ctx, pod_, ele_, _type_):
client = self._get_client(pod_['pod_name'])
return client.list_resources(_type_, t_ctx, [{'key': 'name',
'comparator': 'eq',
'value': _id_}])
if _type_ == constants.RT_NETWORK:
value = utils.get_bottom_network_name(ele_)
else:
value = ele_['id']
return client.list_resources(
_type_, t_ctx,
[{'key': 'name', 'comparator': 'eq',
'value': value}])
def create_resources(t_ctx, q_ctx, pod_, body_, _type_):
client = self._get_client(pod_['pod_name'])

View File

@ -52,8 +52,9 @@ BOTTOM_NETS = BOTTOM1_NETS
BOTTOM_SUBNETS = BOTTOM1_SUBNETS
BOTTOM_PORTS = BOTTOM1_PORTS
BOTTOM_SGS = BOTTOM1_SGS
BOTTOM_SERVERS = []
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_SGS,
RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_SGS, BOTTOM_SERVERS,
BOTTOM1_NETS, BOTTOM1_SUBNETS, BOTTOM1_PORTS, BOTTOM1_SGS,
BOTTOM2_NETS, BOTTOM2_SUBNETS, BOTTOM2_PORTS, BOTTOM2_SGS]
@ -90,7 +91,8 @@ class FakeClient(object):
'bottom': {'network': BOTTOM_NETS,
'subnet': BOTTOM_SUBNETS,
'port': BOTTOM_PORTS,
'security_group': BOTTOM_SGS},
'security_group': BOTTOM_SGS,
'server': BOTTOM_SERVERS},
'bottom2': {'network': BOTTOM2_NETS,
'subnet': BOTTOM2_SUBNETS,
'port': BOTTOM2_PORTS,
@ -224,9 +226,34 @@ class FakeClient(object):
'subnet', ctx,
[{'key': 'id', 'comparator': 'eq', 'value': subnet_id}])[0]
def create_servers(self, ctx, body):
# do nothing here since it will be mocked
pass
def create_servers(self, ctx, **body):
body['id'] = uuidutils.generate_uuid()
BOTTOM_SERVERS.append(body)
return body
def list_servers(self, ctx, filters):
ret_servers = []
for b_server in self.list_resources('server', ctx, filters):
ret_server = copy.deepcopy(b_server)
for nic in ret_server['nics']:
ports = self.list_ports(
ctx, [{'key': 'id', 'comparator': 'eq',
'value': nic['port-id']}])
nets = self.list_resources(
'network', ctx, [{'key': 'id', 'comparator': 'eq',
'value': ports[0]['network_id']}])
ret_server['addresses'] = {
nets[0]['name']: [
{'OS-EXT-IPS-MAC:mac_addr': ports[0]['mac_address'],
'version': 4,
'addr': ports[0]['fixed_ips'][0]['ip_address'],
'OS-EXT-IPS:type': 'fixed'}]}
ret_servers.append(ret_server)
return ret_servers
def get_servers(self, ctx, server_id):
return self.list_servers(
ctx, [{'key': 'id', 'comparator': 'eq', 'value': server_id}])[0]
def get_security_groups(self, ctx, sg_id):
sg = self.list_resources(
@ -399,7 +426,7 @@ class ServerTest(unittest.TestCase):
def test_handle_network(self):
t_pod, b_pod = self._prepare_pod()
net = {'id': 'top_net_id'}
net = {'id': 'top_net_id', 'name': 'net'}
subnet = {'id': 'top_subnet_id',
'network_id': 'top_net_id',
'ip_version': 4,
@ -415,7 +442,7 @@ class ServerTest(unittest.TestCase):
def test_handle_port(self):
t_pod, b_pod = self._prepare_pod()
net = {'id': 'top_net_id'}
net = {'id': 'top_net_id', 'name': 'net'}
subnet = {'id': 'top_subnet_id',
'network_id': 'top_net_id',
'ip_version': 4,
@ -444,7 +471,7 @@ class ServerTest(unittest.TestCase):
bottom_net_id = 'bottom_net_id'
top_subnet_id = 'top_subnet_id'
bottom_subnet_id = 'bottom_subnet_id'
t_net = {'id': top_net_id}
t_net = {'id': top_net_id, 'name': 'net'}
b_net = {'id': bottom_net_id}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
@ -498,6 +525,7 @@ class ServerTest(unittest.TestCase):
def test_handle_network_dhcp_port_exist_diff_ip(self):
self._test_handle_network_dhcp_port('10.0.0.4')
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(pecan, 'abort')
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
@ -506,7 +534,7 @@ class ServerTest(unittest.TestCase):
top_net_id = 'top_net_id'
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_net = {'id': top_net_id, 'name': 'net'}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
@ -569,6 +597,7 @@ class ServerTest(unittest.TestCase):
calls = [mock.call(400, msg), mock.call(400, msg)]
mock_abort.assert_has_calls(calls)
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post(self, mock_ctx, mock_create):
@ -577,7 +606,7 @@ class ServerTest(unittest.TestCase):
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_net = {'id': top_net_id, 'name': 'net'}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
@ -641,6 +670,7 @@ class ServerTest(unittest.TestCase):
if rule['ethertype'] == 'IPv4' and rule['direction'] == 'ingress':
self.assertIsNone(rule['remote_group_id'])
self.assertEqual('10.0.0.0/24', rule['remote_ip_prefix'])
with self.context.session.begin():
routes = core.query_resource(self.context, models.ResourceRouting,
[{'key': 'resource_type',
@ -663,6 +693,7 @@ class ServerTest(unittest.TestCase):
self.assertEqual(b_pod['pod_id'], routes[0]['pod_id'])
self.assertEqual(self.project_id, routes[0]['project_id'])
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post_exception_retry(self, mock_ctx, mock_server):
@ -671,7 +702,7 @@ class ServerTest(unittest.TestCase):
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id}
t_net = {'id': top_net_id, 'name': 'net'}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
@ -750,6 +781,7 @@ class ServerTest(unittest.TestCase):
nics=[{'port-id': bottom_port_id}],
security_groups=[bottom_sg['id']])
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(FakeClient, 'create_servers')
@patch.object(context, 'extract_context_from_environ')
def test_post_across_pods(self, mock_ctx, mock_create):
@ -761,7 +793,7 @@ class ServerTest(unittest.TestCase):
top_subnet2_id = 'top_subnet2_id'
top_sg_id = 'top_sg_id'
t_net1 = {'id': top_net1_id}
t_net1 = {'id': top_net1_id, 'name': 'net1'}
t_subnet1 = {'id': top_subnet1_id,
'tenant_id': self.project_id,
'network_id': top_net1_id,
@ -771,7 +803,7 @@ class ServerTest(unittest.TestCase):
'allocation_pools': {'start': '10.0.1.2',
'end': '10.0.1.254'},
'enable_dhcp': True}
t_net2 = {'id': top_net2_id}
t_net2 = {'id': top_net2_id, 'name': 'net2'}
t_subnet2 = {'id': top_subnet2_id,
'tenant_id': self.project_id,
'network_id': top_net2_id,
@ -1109,6 +1141,72 @@ class ServerTest(unittest.TestCase):
calls = [mock.call(400, msg)]
mock_abort.assert_has_calls(calls)
@patch.object(pecan, 'response', new=FakeResponse)
@patch.object(context, 'extract_context_from_environ')
def test_get(self, mock_ctx):
t_pod, b_pod = self._prepare_pod()
top_net_id = 'top_net_id'
top_subnet_id = 'top_subnet_id'
top_sg_id = 'top_sg_id'
t_net = {'id': top_net_id, 'name': 'net'}
t_subnet = {'id': top_subnet_id,
'network_id': top_net_id,
'ip_version': 4,
'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1',
'allocation_pools': {'start': '10.0.0.2',
'end': '10.0.0.254'},
'enable_dhcp': True}
t_sg = {'id': top_sg_id, 'name': 'default', 'description': '',
'tenant_id': self.project_id,
'security_group_rules': [
{'remote_group_id': top_sg_id,
'direction': 'ingress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
{'remote_group_id': None,
'direction': 'egress',
'remote_ip_prefix': None,
'protocol': None,
'port_range_max': None,
'port_range_min': None,
'ethertype': 'IPv4'},
]}
TOP_NETS.append(t_net)
TOP_SUBNETS.append(t_subnet)
TOP_SGS.append(t_sg)
server_name = 'test_server'
image_id = 'image_id'
flavor_id = 1
body = {
'server': {
'name': server_name,
'imageRef': image_id,
'flavorRef': flavor_id,
'availability_zone': b_pod['az_name'],
'networks': [{'uuid': top_net_id}]
}
}
mock_ctx.return_value = self.context
server_dict = self.controller.post(**body)['server']
ret_server = self.controller.get_one(server_dict['id'])['server']
self.assertEqual(server_name, ret_server['name'])
self.assertEqual(image_id, ret_server['image'])
self.assertEqual(flavor_id, ret_server['flavor'])
self.assertEqual(t_net['name'], ret_server['addresses'].keys()[0])
ret_server = self.controller.get_one('detail')['servers'][0]
self.assertEqual(server_name, ret_server['name'])
self.assertEqual(image_id, ret_server['image'])
self.assertEqual(flavor_id, ret_server['flavor'])
self.assertEqual(t_net['name'], ret_server['addresses'].keys()[0])
def tearDown(self):
core.ModelBase.metadata.drop_all(core.get_engine())
for res in RES_LIST: