diff options
31 files changed, 695 insertions, 708 deletions
diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index 42e86eadd..25d077f27 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -297,19 +297,19 @@ "updated": "2012-08-07T00:00:00+00:00" }, { - "alias": "os-admin-networks", + "alias": "os-networks", "description": "Admin-only Network Management Extension.", "links": [], - "name": "AdminNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1", + "name": "Networks", + "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", "updated": "2011-12-23T00:00:00+00:00" }, { - "alias": "os-networks", + "alias": "os-tenant-networks", "description": "Tenant-based Network Management Extension.", "links": [], - "name": "OSNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", + "name": "OSTenantNetworks", + "namespace": "http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2", "updated": "2011-12-23T00:00:00+00:00" }, { diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index ea0b45a12..b66c3dbe7 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -125,13 +125,13 @@ <extension alias="os-multiple-create" updated="2012-08-07T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1" name="MultipleCreate"> <description>Allow multiple create in the Create Server v1.1 API.</description> </extension> - <extension alias="os-admin-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1" name="AdminNetworks"> + <extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="Networks"> <description>Admin-only Network Management Extension.</description> </extension> <extension alias="os-networks-associate" updated="2012-11-19T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport"> <description>Network association support.</description> </extension> - <extension alias="os-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="OSNetworks"> + <extension alias="os-tenant-networks" updated="2011-12-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2" name="OSTenantNetworks"> <description>Tenant-based Network Management Extension.</description> </extension> <extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses"> diff --git a/doc/api_samples/os-networks/networks-list-res.json b/doc/api_samples/os-tenant-networks/networks-list-res.json index b857e8112..b857e8112 100644 --- a/doc/api_samples/os-networks/networks-list-res.json +++ b/doc/api_samples/os-tenant-networks/networks-list-res.json diff --git a/doc/api_samples/os-networks/networks-post-res.json b/doc/api_samples/os-tenant-networks/networks-post-res.json index 536a9a0a4..536a9a0a4 100644 --- a/doc/api_samples/os-networks/networks-post-res.json +++ b/doc/api_samples/os-tenant-networks/networks-post-res.json diff --git a/nova/api/openstack/compute/contrib/admin_networks.py b/nova/api/openstack/compute/contrib/admin_networks.py deleted file mode 100644 index f5facd601..000000000 --- a/nova/api/openstack/compute/contrib/admin_networks.py +++ /dev/null @@ -1,170 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Grid Dynamics -# Copyright 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 netaddr -import webob -from webob import exc - -from nova.api.openstack import extensions -from nova.api.openstack import wsgi -from nova import exception -from nova import network -from nova.openstack.common import log as logging - -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'admin_networks') -authorize_view = extensions.extension_authorizer('compute', - 'admin_networks:view') - - -def network_dict(context, network): - fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2', - 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6') - admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted', - 'injected', 'bridge', 'vlan', 'vpn_public_address', - 'vpn_public_port', 'vpn_private_address', 'dhcp_start', - 'project_id', 'host', 'bridge_interface', 'multi_host', - 'priority', 'rxtx_base') - if network: - # NOTE(mnaser): We display a limited set of fields so users can know - # what networks are available, extra system-only fields - # are only visible if they are an admin. - if context.is_admin: - fields += admin_fields - result = dict((field, network[field]) for field in fields) - if 'uuid' in network: - result['id'] = network['uuid'] - return result - else: - return {} - - -class AdminNetworkController(wsgi.Controller): - - def __init__(self, network_api=None): - self.network_api = network_api or network.API() - - def index(self, req): - context = req.environ['nova.context'] - authorize_view(context) - networks = self.network_api.get_all(context) - result = [network_dict(context, net_ref) for net_ref in networks] - return {'networks': result} - - @wsgi.action("disassociate") - def _disassociate_host_and_project(self, req, id, body): - context = req.environ['nova.context'] - authorize(context) - LOG.debug(_("Disassociating network with id %s"), id) - - try: - self.network_api.associate(context, id, host=None, project=None) - except exception.NetworkNotFound: - raise exc.HTTPNotFound(_("Network not found")) - return exc.HTTPAccepted() - - def show(self, req, id): - context = req.environ['nova.context'] - authorize_view(context) - LOG.debug(_("Showing network with id %s") % id) - try: - network = self.network_api.get(context, id) - except exception.NetworkNotFound: - raise exc.HTTPNotFound(_("Network not found")) - return {'network': network_dict(context, network)} - - def delete(self, req, id): - context = req.environ['nova.context'] - authorize(context) - LOG.info(_("Deleting network with id %s") % id) - try: - self.network_api.delete(context, id) - except exception.NetworkNotFound: - raise exc.HTTPNotFound(_("Network not found")) - return exc.HTTPAccepted() - - def create(self, req, body): - context = req.environ['nova.context'] - authorize(context) - - def bad(e): - return exc.HTTPUnprocessableEntity(explanation=e) - - if not (body and body.get("network")): - raise bad(_("Missing network in body")) - - params = body["network"] - if not params.get("label"): - raise bad(_("Network label is required")) - - cidr = params.get("cidr") or params.get("cidr_v6") - if not cidr: - raise bad(_("Network cidr or cidr_v6 is required")) - - LOG.debug(_("Creating network with label %s") % params["label"]) - - params["num_networks"] = 1 - params["network_size"] = netaddr.IPNetwork(cidr).size - - network = self.network_api.create(context, **params)[0] - return {"network": network_dict(context, network)} - - def add(self, req, body): - context = req.environ['nova.context'] - authorize(context) - if not body: - raise exc.HTTPUnprocessableEntity() - - network_id = body.get('id', None) - project_id = context.project_id - LOG.debug(_("Associating network %(network)s" - " with project %(project)s") % - {"network": network_id or "", - "project": project_id}) - try: - self.network_api.add_network_to_project( - context, project_id, network_id) - except Exception as ex: - msg = (_("Cannot associate network %(network)s" - " with project %(project)s: %(message)s") % - {"network": network_id or "", - "project": project_id, - "message": getattr(ex, "value", str(ex))}) - raise exc.HTTPBadRequest(explanation=msg) - - return webob.Response(status_int=202) - - -class Admin_networks(extensions.ExtensionDescriptor): - """Admin-only Network Management Extension.""" - - name = "AdminNetworks" - alias = "os-admin-networks" - namespace = ("http://docs.openstack.org/compute/" - "ext/os-admin-networks/api/v1.1") - updated = "2011-12-23T00:00:00+00:00" - - def get_resources(self): - member_actions = {'action': 'POST'} - collection_actions = {'add': 'POST'} - res = extensions.ResourceExtension( - 'os-admin-networks', - AdminNetworkController(), - member_actions=member_actions, - collection_actions=collection_actions) - return [res] diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 52487c305..d1b39d6db 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -124,10 +124,17 @@ class HostController(object): """ context = req.environ['nova.context'] authorize(context) + filters = {} zone = req.GET.get('zone', None) - data = self.api.list_hosts(context, zone) - - return {'hosts': data} + if zone: + filters['availability_zone'] = zone + services = self.api.service_get_all(context, filters=filters) + hosts = [] + for service in services: + hosts.append({'host_name': service['host'], + 'service': service['topic'], + 'zone': service['availability_zone']}) + return {'hosts': hosts} @wsgi.serializers(xml=HostUpdateTemplate) @wsgi.deserializers(xml=HostUpdateDeserializer) @@ -243,6 +250,55 @@ class HostController(object): def reboot(self, req, id): return self._host_power_action(req, host_name=id, action="reboot") + @staticmethod + def _get_total_resources(host_name, compute_node): + return {'resource': {'host': host_name, + 'project': '(total)', + 'cpu': compute_node['vcpus'], + 'memory_mb': compute_node['memory_mb'], + 'disk_gb': compute_node['local_gb']}} + + @staticmethod + def _get_used_now_resources(host_name, compute_node): + return {'resource': {'host': host_name, + 'project': '(used_now)', + 'cpu': compute_node['vcpus_used'], + 'memory_mb': compute_node['memory_mb_used'], + 'disk_gb': compute_node['local_gb_used']}} + + @staticmethod + def _get_resource_totals_from_instances(host_name, instances): + cpu_sum = 0 + mem_sum = 0 + hdd_sum = 0 + for instance in instances: + cpu_sum += instance['vcpus'] + mem_sum += instance['memory_mb'] + hdd_sum += instance['root_gb'] + instance['ephemeral_gb'] + + return {'resource': {'host': host_name, + 'project': '(used_max)', + 'cpu': cpu_sum, + 'memory_mb': mem_sum, + 'disk_gb': hdd_sum}} + + @staticmethod + def _get_resources_by_project(host_name, instances): + # Getting usage resource per project + project_map = {} + for instance in instances: + resource = project_map.setdefault(instance['project_id'], + {'host': host_name, + 'project': instance['project_id'], + 'cpu': 0, + 'memory_mb': 0, + 'disk_gb': 0}) + resource['cpu'] += instance['vcpus'] + resource['memory_mb'] += instance['memory_mb'] + resource['disk_gb'] += (instance['root_gb'] + + instance['ephemeral_gb']) + return project_map + @wsgi.serializers(xml=HostShowTemplate) def show(self, req, id): """Shows the physical/usage resource given by hosts. @@ -256,14 +312,26 @@ class HostController(object): 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30} """ context = req.environ['nova.context'] + host_name = id try: - data = self.api.describe_host(context, id) + service = self.api.service_get_by_compute_host(context, host_name) except exception.NotFound as e: raise webob.exc.HTTPNotFound(explanation=e.message) except exception.AdminRequired: msg = _("Describe-resource is admin only functionality") raise webob.exc.HTTPForbidden(explanation=msg) - return {'host': data} + compute_node = service['compute_node'][0] + instances = self.api.instance_get_all_by_host(context, host_name) + resources = [self._get_total_resources(host_name, compute_node)] + resources.append(self._get_used_now_resources(host_name, + compute_node)) + resources.append(self._get_resource_totals_from_instances(host_name, + instances)) + by_proj_resources = self._get_resources_by_project(host_name, + instances) + for resource in by_proj_resources.itervalues(): + resources.append({'resource': resource}) + return {'host': resources} class Hosts(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/compute/contrib/networks_associate.py b/nova/api/openstack/compute/contrib/networks_associate.py index 4990c1b5e..3cdda1d76 100644 --- a/nova/api/openstack/compute/contrib/networks_associate.py +++ b/nova/api/openstack/compute/contrib/networks_associate.py @@ -62,6 +62,6 @@ class Networks_associate(extensions.ExtensionDescriptor): def get_controller_extensions(self): extension = extensions.ControllerExtension( - self, 'os-admin-networks', NetworkAssociateActionController()) + self, 'os-networks', NetworkAssociateActionController()) return [extension] diff --git a/nova/api/openstack/compute/contrib/os_networks.py b/nova/api/openstack/compute/contrib/os_networks.py index 4be0bd100..d1d172686 100644 --- a/nova/api/openstack/compute/contrib/os_networks.py +++ b/nova/api/openstack/compute/contrib/os_networks.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2013 OpenStack LLC. +# Copyright 2011 Grid Dynamics +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -15,199 +16,155 @@ # License for the specific language governing permissions and limitations # under the License. - import netaddr -import netaddr.core as netexc +import webob from webob import exc from nova.api.openstack import extensions -from nova import context as nova_context +from nova.api.openstack import wsgi from nova import exception -import nova.network -from nova.openstack.common import cfg +from nova import network from nova.openstack.common import log as logging -from nova import quota - - -CONF = cfg.CONF - -try: - os_network_opts = [ - cfg.BoolOpt("enable_network_quota", - default=False, - help="Enables or disables quotaing of tenant networks"), - cfg.StrOpt('use_quantum_default_nets', - default="False", - help=('Control for checking for default networks')), - cfg.StrOpt('quantum_default_tenant_id', - default="default", - help=('Default tenant id when creating quantum ' - 'networks')) - ] - CONF.register_opts(os_network_opts) -except cfg.DuplicateOptError: - # NOTE(jkoelker) These options are verbatim elsewhere this is here - # to make sure they are registered for our use. - pass - -if CONF.enable_network_quota: - opts = [ - cfg.IntOpt('quota_networks', - default=3, - help='number of private networks allowed per project'), - ] - CONF.register_opts(opts) - -QUOTAS = quota.QUOTAS -LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('compute', 'os-networks') - - -def network_dict(network): - return {"id": network.get("uuid") or network["id"], - "cidr": network["cidr"], - "label": network["label"]} +LOG = logging.getLogger(__name__) +authorize = extensions.extension_authorizer('compute', 'networks') +authorize_view = extensions.extension_authorizer('compute', + 'networks:view') + + +def network_dict(context, network): + fields = ('id', 'cidr', 'netmask', 'gateway', 'broadcast', 'dns1', 'dns2', + 'cidr_v6', 'gateway_v6', 'label', 'netmask_v6') + admin_fields = ('created_at', 'updated_at', 'deleted_at', 'deleted', + 'injected', 'bridge', 'vlan', 'vpn_public_address', + 'vpn_public_port', 'vpn_private_address', 'dhcp_start', + 'project_id', 'host', 'bridge_interface', 'multi_host', + 'priority', 'rxtx_base') + if network: + # NOTE(mnaser): We display a limited set of fields so users can know + # what networks are available, extra system-only fields + # are only visible if they are an admin. + if context.is_admin: + fields += admin_fields + result = dict((field, network[field]) for field in fields) + if 'uuid' in network: + result['id'] = network['uuid'] + return result + else: + return {} + + +class NetworkController(wsgi.Controller): -class NetworkController(object): def __init__(self, network_api=None): - self.network_api = nova.network.API() - self._default_networks = [] - - def _refresh_default_networks(self): - self._default_networks = [] - if CONF.use_quantum_default_nets == "True": - try: - self._default_networks = self._get_default_networks() - except Exception: - LOG.exception("Failed to get default networks") - - def _get_default_networks(self): - project_id = CONF.quantum_default_tenant_id - ctx = nova_context.RequestContext(user_id=None, - project_id=project_id) - networks = {} - for n in self.network_api.get_all(ctx): - networks[n['id']] = n['label'] - return [{'id': k, 'label': v} for k, v in networks.iteritems()] + self.network_api = network_api or network.API() def index(self, req): context = req.environ['nova.context'] - authorize(context) + authorize_view(context) networks = self.network_api.get_all(context) - if not self._default_networks: - self._refresh_default_networks() - networks.extend(self._default_networks) - return {'networks': [network_dict(n) for n in networks]} + result = [network_dict(context, net_ref) for net_ref in networks] + return {'networks': result} - def show(self, req, id): + @wsgi.action("disassociate") + def _disassociate_host_and_project(self, req, id, body): context = req.environ['nova.context'] authorize(context) + LOG.debug(_("Disassociating network with id %s"), id) + + try: + self.network_api.associate(context, id, host=None, project=None) + except exception.NetworkNotFound: + raise exc.HTTPNotFound(_("Network not found")) + return exc.HTTPAccepted() + + def show(self, req, id): + context = req.environ['nova.context'] + authorize_view(context) LOG.debug(_("Showing network with id %s") % id) try: network = self.network_api.get(context, id) except exception.NetworkNotFound: raise exc.HTTPNotFound(_("Network not found")) - return network_dict(network) + return {'network': network_dict(context, network)} def delete(self, req, id): context = req.environ['nova.context'] authorize(context) - try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=-1) - except Exception: - reservation = None - LOG.exception(_("Failed to update usages deallocating " - "network.")) - LOG.info(_("Deleting network with id %s") % id) - try: self.network_api.delete(context, id) - if CONF.enable_network_quota and reservation: - QUOTAS.commit(context, reservation) - response = exc.HTTPAccepted() except exception.NetworkNotFound: - response = exc.HTTPNotFound(_("Network not found")) - - return response + raise exc.HTTPNotFound(_("Network not found")) + return exc.HTTPAccepted() def create(self, req, body): - if not body: - raise exc.HTTPUnprocessableEntity() - - context = req.environ["nova.context"] + context = req.environ['nova.context'] authorize(context) - network = body["network"] - keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size", - "num_networks"] - kwargs = dict((k, network.get(k)) for k in keys) + def bad(e): + return exc.HTTPUnprocessableEntity(explanation=e) - label = network["label"] + if not (body and body.get("network")): + raise bad(_("Missing network in body")) - if not (kwargs["cidr"] or kwargs["cidr_v6"]): - msg = _("No CIDR requested") - raise exc.HTTPBadRequest(explanation=msg) - if kwargs["cidr"]: - try: - net = netaddr.IPNetwork(kwargs["cidr"]) - if net.size < 4: - msg = _("Requested network does not contain " - "enough (2+) usable hosts") - raise exc.HTTPBadRequest(explanation=msg) - except netexc.AddrFormatError: - msg = _("CIDR is malformed.") - raise exc.HTTPBadRequest(explanation=msg) - except netexc.AddrConversionError: - msg = _("Address could not be converted.") - raise exc.HTTPBadRequest(explanation=msg) - - networks = [] + params = body["network"] + if not params.get("label"): + raise bad(_("Network label is required")) + + cidr = params.get("cidr") or params.get("cidr_v6") + if not cidr: + raise bad(_("Network cidr or cidr_v6 is required")) + + LOG.debug(_("Creating network with label %s") % params["label"]) + + params["num_networks"] = 1 + params["network_size"] = netaddr.IPNetwork(cidr).size + + network = self.network_api.create(context, **params)[0] + return {"network": network_dict(context, network)} + + def add(self, req, body): + context = req.environ['nova.context'] + authorize(context) + if not body: + raise exc.HTTPUnprocessableEntity() + + network_id = body.get('id', None) + project_id = context.project_id + LOG.debug(_("Associating network %(network)s" + " with project %(project)s") % + {"network": network_id or "", + "project": project_id}) try: - if CONF.enable_network_quota: - reservation = QUOTAS.reserve(context, networks=1) - except exception.OverQuota: - msg = _("Quota exceeded, too many networks.") + self.network_api.add_network_to_project( + context, project_id, network_id) + except Exception as ex: + msg = (_("Cannot associate network %(network)s" + " with project %(project)s: %(message)s") % + {"network": network_id or "", + "project": project_id, + "message": getattr(ex, "value", str(ex))}) raise exc.HTTPBadRequest(explanation=msg) - try: - networks = self.network_api.create(context, - label=label, **kwargs) - if CONF.enable_network_quota: - QUOTAS.commit(context, reservation) - except Exception: - if CONF.enable_network_quota: - QUOTAS.rollback(context, reservation) - msg = _("Create networks failed") - LOG.exception(msg, extra=network) - raise exc.HTTPServiceUnavailable(explanation=msg) - return {"network": network_dict(networks[0])} + return webob.Response(status_int=202) class Os_networks(extensions.ExtensionDescriptor): - """Tenant-based Network Management Extension.""" + """Admin-only Network Management Extension.""" - name = "OSNetworks" + name = "Networks" alias = "os-networks" - namespace = "http://docs.openstack.org/compute/ext/os-networks/api/v1.1" - updated = "2012-03-07T09:46:43-05:00" + namespace = ("http://docs.openstack.org/compute/" + "ext/os-networks/api/v1.1") + updated = "2011-12-23T00:00:00+00:00" def get_resources(self): - ext = extensions.ResourceExtension('os-networks', - NetworkController()) - return [ext] - - -def _sync_networks(context, project_id, session): - ctx = nova_context.RequestContext(user_id=None, project_id=project_id) - ctx = ctx.elevated() - networks = nova.network.api.API().get_all(ctx) - return dict(networks=len(networks)) - - -if CONF.enable_network_quota: - QUOTAS.register_resource(quota.ReservableResource('networks', - _sync_networks, - 'quota_networks')) + member_actions = {'action': 'POST'} + collection_actions = {'add': 'POST'} + res = extensions.ResourceExtension( + 'os-networks', + NetworkController(), + member_actions=member_actions, + collection_actions=collection_actions) + return [res] diff --git a/nova/api/openstack/compute/contrib/os_tenant_networks.py b/nova/api/openstack/compute/contrib/os_tenant_networks.py new file mode 100644 index 000000000..03178ab65 --- /dev/null +++ b/nova/api/openstack/compute/contrib/os_tenant_networks.py @@ -0,0 +1,214 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 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 netaddr +import netaddr.core as netexc +from webob import exc + +from nova.api.openstack import extensions +from nova import context as nova_context +from nova import exception +import nova.network +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova import quota + + +CONF = cfg.CONF + +try: + os_network_opts = [ + cfg.BoolOpt("enable_network_quota", + default=False, + help="Enables or disables quotaing of tenant networks"), + cfg.StrOpt('use_quantum_default_nets', + default="False", + help=('Control for checking for default networks')), + cfg.StrOpt('quantum_default_tenant_id', + default="default", + help=('Default tenant id when creating quantum ' + 'networks')) + ] + CONF.register_opts(os_network_opts) +except cfg.DuplicateOptError: + # NOTE(jkoelker) These options are verbatim elsewhere this is here + # to make sure they are registered for our use. + pass + +if CONF.enable_network_quota: + opts = [ + cfg.IntOpt('quota_networks', + default=3, + help='number of private networks allowed per project'), + ] + CONF.register_opts(opts) + +QUOTAS = quota.QUOTAS +LOG = logging.getLogger(__name__) +authorize = extensions.extension_authorizer('compute', 'os-tenant-networks') + + +def network_dict(network): + return {"id": network.get("uuid") or network["id"], + "cidr": network["cidr"], + "label": network["label"]} + + +class NetworkController(object): + def __init__(self, network_api=None): + self.network_api = nova.network.API() + self._default_networks = [] + + def _refresh_default_networks(self): + self._default_networks = [] + if CONF.use_quantum_default_nets == "True": + try: + self._default_networks = self._get_default_networks() + except Exception: + LOG.exception("Failed to get default networks") + + def _get_default_networks(self): + project_id = CONF.quantum_default_tenant_id + ctx = nova_context.RequestContext(user_id=None, + project_id=project_id) + networks = {} + for n in self.network_api.get_all(ctx): + networks[n['id']] = n['label'] + return [{'id': k, 'label': v} for k, v in networks.iteritems()] + + def index(self, req): + context = req.environ['nova.context'] + authorize(context) + networks = self.network_api.get_all(context) + if not self._default_networks: + self._refresh_default_networks() + networks.extend(self._default_networks) + return {'networks': [network_dict(n) for n in networks]} + + def show(self, req, id): + context = req.environ['nova.context'] + authorize(context) + LOG.debug(_("Showing network with id %s") % id) + try: + network = self.network_api.get(context, id) + except exception.NetworkNotFound: + raise exc.HTTPNotFound(_("Network not found")) + return network_dict(network) + + def delete(self, req, id): + context = req.environ['nova.context'] + authorize(context) + try: + if CONF.enable_network_quota: + reservation = QUOTAS.reserve(context, networks=-1) + except Exception: + reservation = None + LOG.exception(_("Failed to update usages deallocating " + "network.")) + + LOG.info(_("Deleting network with id %s") % id) + + try: + self.network_api.delete(context, id) + if CONF.enable_network_quota and reservation: + QUOTAS.commit(context, reservation) + response = exc.HTTPAccepted() + except exception.NetworkNotFound: + response = exc.HTTPNotFound(_("Network not found")) + + return response + + def create(self, req, body): + if not body: + raise exc.HTTPUnprocessableEntity() + + context = req.environ["nova.context"] + authorize(context) + + network = body["network"] + keys = ["cidr", "cidr_v6", "ipam", "vlan_start", "network_size", + "num_networks"] + kwargs = dict((k, network.get(k)) for k in keys) + + label = network["label"] + + if not (kwargs["cidr"] or kwargs["cidr_v6"]): + msg = _("No CIDR requested") + raise exc.HTTPBadRequest(explanation=msg) + if kwargs["cidr"]: + try: + net = netaddr.IPNetwork(kwargs["cidr"]) + if net.size < 4: + msg = _("Requested network does not contain " + "enough (2+) usable hosts") + raise exc.HTTPBadRequest(explanation=msg) + except netexc.AddrFormatError: + msg = _("CIDR is malformed.") + raise exc.HTTPBadRequest(explanation=msg) + except netexc.AddrConversionError: + msg = _("Address could not be converted.") + raise exc.HTTPBadRequest(explanation=msg) + + networks = [] + try: + if CONF.enable_network_quota: + reservation = QUOTAS.reserve(context, networks=1) + except exception.OverQuota: + msg = _("Quota exceeded, too many networks.") + raise exc.HTTPBadRequest(explanation=msg) + + try: + networks = self.network_api.create(context, + label=label, **kwargs) + if CONF.enable_network_quota: + QUOTAS.commit(context, reservation) + except Exception: + if CONF.enable_network_quota: + QUOTAS.rollback(context, reservation) + msg = _("Create networks failed") + LOG.exception(msg, extra=network) + raise exc.HTTPServiceUnavailable(explanation=msg) + return {"network": network_dict(networks[0])} + + +class Os_tenant_networks(extensions.ExtensionDescriptor): + """Tenant-based Network Management Extension.""" + + name = "OSTenantNetworks" + alias = "os-tenant-networks" + namespace = ("http://docs.openstack.org/compute/" + "ext/os-tenant-networks/api/v2") + updated = "2012-03-07T09:46:43-05:00" + + def get_resources(self): + ext = extensions.ResourceExtension('os-tenant-networks', + NetworkController()) + return [ext] + + +def _sync_networks(context, project_id, session): + ctx = nova_context.RequestContext(user_id=None, project_id=project_id) + ctx = ctx.elevated() + networks = nova.network.api.API().get_all(ctx) + return dict(networks=len(networks)) + + +if CONF.enable_network_quota: + QUOTAS.register_resource(quota.ReservableResource('networks', + _sync_networks, + 'quota_networks')) diff --git a/nova/api/openstack/compute/contrib/services.py b/nova/api/openstack/compute/contrib/services.py index c792c72da..2786ad814 100644 --- a/nova/api/openstack/compute/contrib/services.py +++ b/nova/api/openstack/compute/contrib/services.py @@ -21,6 +21,7 @@ import webob.exc from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil +from nova import availability_zones from nova import db from nova import exception from nova.openstack.common import cfg @@ -69,6 +70,7 @@ class ServiceController(object): authorize(context) now = timeutils.utcnow() services = db.service_get_all(context) + services = availability_zones.set_availability_zones(context, services) host = '' if 'host' in req.GET: diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index d3a6fc8a9..f0fdb5a15 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -538,10 +538,10 @@ class Controller(wsgi.Controller): marker=marker) except exception.MarkerNotFound as e: msg = _('marker [%s] not found') % marker - raise webob.exc.HTTPBadRequest(explanation=msg) + raise exc.HTTPBadRequest(explanation=msg) except exception.FlavorNotFound as e: msg = _("Flavor could not be found") - raise webob.exc.HTTPUnprocessableEntity(explanation=msg) + raise exc.HTTPUnprocessableEntity(explanation=msg) if is_detail: self._add_instance_faults(context, instance_list) @@ -828,21 +828,24 @@ class Controller(wsgi.Controller): try: min_count = int(min_count) except ValueError: - raise webob.exc.HTTPBadRequest(_('min_count must be an ' - 'integer value')) + msg = _('min_count must be an integer value') + raise exc.HTTPBadRequest(explanation=msg) if min_count < 1: - raise webob.exc.HTTPBadRequest(_('min_count must be > 0')) + msg = _('min_count must be > 0') + raise exc.HTTPBadRequest(explanation=msg) try: max_count = int(max_count) except ValueError: - raise webob.exc.HTTPBadRequest(_('max_count must be an ' - 'integer value')) + msg = _('max_count must be an integer value') + raise exc.HTTPBadRequest(explanation=msg) if max_count < 1: - raise webob.exc.HTTPBadRequest(_('max_count must be > 0')) + msg = _('max_count must be > 0') + raise exc.HTTPBadRequest(explanation=msg) if min_count > max_count: - raise webob.exc.HTTPBadRequest(_('min_count must be <= max_count')) + msg = _('min_count must be <= max_count') + raise exc.HTTPBadRequest(explanation=msg) auto_disk_config = False if self.ext_mgr.is_loaded('OS-DCF'): @@ -1202,7 +1205,8 @@ class Controller(wsgi.Controller): try: body = body['rebuild'] except (KeyError, TypeError): - raise exc.HTTPBadRequest(_("Invalid request body")) + msg = _('Invalid request body') + raise exc.HTTPBadRequest(explanation=msg) try: image_href = body["imageRef"] diff --git a/nova/compute/api.py b/nova/compute/api.py index bddb83449..7770bc9e6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -92,6 +92,7 @@ CONF = cfg.CONF CONF.register_opts(compute_opts) CONF.import_opt('compute_topic', 'nova.compute.rpcapi') CONF.import_opt('consoleauth_topic', 'nova.consoleauth') +CONF.import_opt('enable', 'nova.cells.opts', group='cells') MAX_USERDATA_SIZE = 65535 QUOTAS = quota.QUOTAS @@ -1547,12 +1548,9 @@ class API(base.Base): elevated = context.elevated() block_info = self._get_block_device_info(elevated, instance['uuid']) - network_info = self.network_api.get_instance_nw_info(elevated, - instance) self.compute_rpcapi.reboot_instance(context, instance=instance, block_device_info=block_info, - network_info=network_info, reboot_type=reboot_type) def _get_image(self, context, image_href): @@ -1665,6 +1663,11 @@ class API(base.Base): self.db.migration_update(elevated, migration_ref['id'], {'status': 'reverting'}) + # With cells, the best we can do right now is commit the reservations + # immediately... + if CONF.cells.enable and reservations: + QUOTAS.commit(context, reservations) + reservations = [] self.compute_rpcapi.revert_resize(context, instance=instance, migration=migration_ref, @@ -1689,6 +1692,11 @@ class API(base.Base): self.db.migration_update(elevated, migration_ref['id'], {'status': 'confirming'}) + # With cells, the best we can do right now is commit the reservations + # immediately... + if CONF.cells.enable and reservations: + QUOTAS.commit(context, reservations) + reservations = [] self.compute_rpcapi.confirm_resize(context, instance=instance, migration=migration_ref, @@ -1851,6 +1859,12 @@ class API(base.Base): if not CONF.allow_resize_to_same_host: filter_properties['ignore_hosts'].append(instance['host']) + # With cells, the best we can do right now is commit the reservations + # immediately... + if CONF.cells.enable and reservations: + QUOTAS.commit(context, reservations) + reservations = [] + args = { "instance": instance, "instance_type": new_instance_type, @@ -2185,139 +2199,76 @@ class API(base.Base): disk_over_commit, instance, host_name) -def check_host(fn): - """Decorator that makes sure that the host exists.""" - def wrapped(self, context, host_name, *args, **kwargs): - if self.does_host_exist(context, host_name): - return fn(self, context, host_name, *args, **kwargs) - else: - raise exception.HostNotFound(host=host_name) - return wrapped - - class HostAPI(base.Base): """Sub-set of the Compute Manager API for managing host operations.""" - def __init__(self): - self.compute_rpcapi = compute_rpcapi.ComputeAPI() + def __init__(self, rpcapi=None): + self.rpcapi = rpcapi or compute_rpcapi.ComputeAPI() super(HostAPI, self).__init__() - @check_host + def _assert_host_exists(self, context, host_name): + """Raise HostNotFound if compute host doesn't exist.""" + if not self.db.service_get_by_host_and_topic(context, host_name, + CONF.compute_topic): + raise exception.HostNotFound(host=host_name) + def set_host_enabled(self, context, host_name, enabled): """Sets the specified host's ability to accept new instances.""" # NOTE(comstud): No instance_uuid argument to this compute manager # call - return self.compute_rpcapi.set_host_enabled(context, enabled=enabled, + self._assert_host_exists(context, host_name) + return self.rpcapi.set_host_enabled(context, enabled=enabled, host=host_name) - @check_host def get_host_uptime(self, context, host_name): """Returns the result of calling "uptime" on the target host.""" # NOTE(comstud): No instance_uuid argument to this compute manager # call - return self.compute_rpcapi.get_host_uptime(context, host=host_name) + self._assert_host_exists(context, host_name) + return self.rpcapi.get_host_uptime(context, host=host_name) - @check_host def host_power_action(self, context, host_name, action): """Reboots, shuts down or powers up the host.""" - return self.compute_rpcapi.host_power_action(context, action=action, + self._assert_host_exists(context, host_name) + return self.rpcapi.host_power_action(context, action=action, host=host_name) - def list_hosts(self, context, zone=None, service=None): - """Returns a summary list of enabled hosts, optionally filtering - by zone and/or service type. + def set_host_maintenance(self, context, host_name, mode): + """Start/Stop host maintenance window. On start, it triggers + guest VMs evacuation.""" + self._assert_host_exists(context, host_name) + return self.rpcapi.host_maintenance_mode(context, + host_param=host_name, mode=mode, host=host_name) + + def service_get_all(self, context, filters=None): + """Returns a list of services, optionally filtering the results. + + If specified, 'filters' should be a dictionary containing services + attributes and matching values. Ie, to get a list of services for + the 'compute' topic, use filters={'topic': 'compute'}. """ - LOG.debug(_("Listing hosts")) + if filters is None: + filters = {} services = self.db.service_get_all(context, False) - services = availability_zones.set_availability_zones(context, services) - if zone: - services = [s for s in services if s['availability_zone'] == zone] - hosts = [] - for host in services: - hosts.append({'host_name': host['host'], 'service': host['topic'], - 'zone': host['availability_zone']}) - if service: - hosts = [host for host in hosts - if host["service"] == service] - return hosts - - def does_host_exist(self, context, host_name): - """ - Returns True if the host with host_name exists, False otherwise - """ - return self.db.service_does_host_exist(context, host_name) + services = availability_zones.set_availability_zones(context, + services) + ret_services = [] + for service in services: + for key, val in filters.iteritems(): + if service[key] != val: + break + else: + # All filters matched. + ret_services.append(service) + return ret_services - def describe_host(self, context, host_name): - """ - Returns information about a host in this kind of format: - :returns: - ex.:: - {'host': 'hostname', - 'project': 'admin', - 'cpu': 1, - 'memory_mb': 2048, - 'disk_gb': 30} - """ - # Getting compute node info and related instances info - try: - compute_ref = self.db.service_get_by_compute_host(context, - host_name) - except exception.ComputeHostNotFound: - raise exception.HostNotFound(host=host_name) - instance_refs = self.db.instance_get_all_by_host(context, - compute_ref['host']) - - # Getting total available/used resource - compute_ref = compute_ref['compute_node'][0] - resources = [{'resource': {'host': host_name, 'project': '(total)', - 'cpu': compute_ref['vcpus'], - 'memory_mb': compute_ref['memory_mb'], - 'disk_gb': compute_ref['local_gb']}}, - {'resource': {'host': host_name, 'project': '(used_now)', - 'cpu': compute_ref['vcpus_used'], - 'memory_mb': compute_ref['memory_mb_used'], - 'disk_gb': compute_ref['local_gb_used']}}] - - cpu_sum = 0 - mem_sum = 0 - hdd_sum = 0 - for i in instance_refs: - cpu_sum += i['vcpus'] - mem_sum += i['memory_mb'] - hdd_sum += i['root_gb'] + i['ephemeral_gb'] - - resources.append({'resource': {'host': host_name, - 'project': '(used_max)', - 'cpu': cpu_sum, - 'memory_mb': mem_sum, - 'disk_gb': hdd_sum}}) - - # Getting usage resource per project - project_ids = [i['project_id'] for i in instance_refs] - project_ids = list(set(project_ids)) - for project_id in project_ids: - vcpus = [i['vcpus'] for i in instance_refs - if i['project_id'] == project_id] - - mem = [i['memory_mb'] for i in instance_refs - if i['project_id'] == project_id] - - disk = [i['root_gb'] + i['ephemeral_gb'] for i in instance_refs - if i['project_id'] == project_id] - - resources.append({'resource': {'host': host_name, - 'project': project_id, - 'cpu': sum(vcpus), - 'memory_mb': sum(mem), - 'disk_gb': sum(disk)}}) - return resources - - @check_host - def set_host_maintenance(self, context, host, mode): - """Start/Stop host maintenance window. On start, it triggers - guest VMs evacuation.""" - return self.compute_rpcapi.host_maintenance_mode(context, - host_param=host, mode=mode, host=host) + def service_get_by_compute_host(self, context, host_name): + """Get service entry for the given compute hostname.""" + return self.db.service_get_by_compute_host(context, host_name) + + def instance_get_all_by_host(self, context, host_name): + """Return all instances on the given host.""" + return self.db.instance_get_all_by_host(context, host_name) class AggregateAPI(base.Base): diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index 47d60aec4..d547c363a 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -18,7 +18,7 @@ from nova import block_device from nova.cells import rpcapi as cells_rpcapi from nova.compute import api as compute_api -from nova.compute import task_states +from nova.compute import instance_types from nova.compute import vm_states from nova import exception from nova.openstack.common import excutils @@ -254,22 +254,14 @@ class ComputeCellsAPI(compute_api.API): @validate_cell def revert_resize(self, context, instance): """Reverts a resize, deleting the 'new' instance in the process.""" - # NOTE(markwash): regular api manipulates the migration here, but we - # don't have access to it. So to preserve the interface just update the - # vm and task state. - self.update(context, instance, - task_state=task_states.RESIZE_REVERTING) + super(ComputeCellsAPI, self).revert_resize(context, instance) self._cast_to_cells(context, instance, 'revert_resize') @check_instance_state(vm_state=[vm_states.RESIZED]) @validate_cell def confirm_resize(self, context, instance): """Confirms a migration/resize and deletes the 'old' instance.""" - # NOTE(markwash): regular api manipulates migration here, but we don't - # have the migration in the api database. So to preserve the interface - # just update the vm and task state without calling super() - self.update(context, instance, task_state=None, - vm_state=vm_states.ACTIVE) + super(ComputeCellsAPI, self).confirm_resize(context, instance) self._cast_to_cells(context, instance, 'confirm_resize') @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], @@ -282,8 +274,36 @@ class ComputeCellsAPI(compute_api.API): the original flavor_id. If flavor_id is not None, the instance should be migrated to a new host and resized to the new flavor_id. """ - super(ComputeCellsAPI, self).resize(context, instance, *args, - **kwargs) + super(ComputeCellsAPI, self).resize(context, instance, *args, **kwargs) + + # NOTE(johannes): If we get to this point, then we know the + # specified flavor_id is valid and exists. We'll need to load + # it again, but that should be safe. + + old_instance_type_id = instance['instance_type_id'] + old_instance_type = instance_types.get_instance_type( + old_instance_type_id) + + flavor_id = kwargs.get('flavor_id') + + if not flavor_id: + new_instance_type = old_instance_type + else: + new_instance_type = instance_types.get_instance_type_by_flavor_id( + flavor_id) + + # NOTE(johannes): Later, when the resize is confirmed or reverted, + # the superclass implementations of those methods will need access + # to a local migration record for quota reasons. We don't need + # source and/or destination information, just the old and new + # instance_types. Status is set to 'finished' since nothing else + # will update the status along the way. + self.db.migration_create(context.elevated(), + {'instance_uuid': instance['uuid'], + 'old_instance_type_id': old_instance_type['id'], + 'new_instance_type_id': new_instance_type['id'], + 'status': 'finished'}) + # FIXME(comstud): pass new instance_type object down to a method # that'll unfold it self._cast_to_cells(context, instance, 'resize', *args, **kwargs) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index a4f17ee3d..3bf8e61ef 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -293,7 +293,7 @@ class ComputeVirtAPI(virtapi.VirtAPI): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '2.22' + RPC_API_VERSION = '2.23' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -1440,19 +1440,14 @@ class ComputeManager(manager.SchedulerDependentManager): if block_device_info is None: block_device_info = self._get_instance_volume_block_device_info( context, instance) - # NOTE(danms): remove this when RPC API < 2.5 compatibility - # is no longer needed - if network_info is None: - network_info = self._get_instance_nw_info(context, instance) - else: - network_info = network_model.NetworkInfo.hydrate(network_info) + network_info = self._get_instance_nw_info(context, instance) self._notify_about_instance_usage(context, instance, "reboot.start") current_power_state = self._get_power_state(context, instance) - self._instance_update(context, instance['uuid'], - power_state=current_power_state, - vm_state=vm_states.ACTIVE) + instance = self._instance_update(context, instance['uuid'], + power_state=current_power_state, + vm_state=vm_states.ACTIVE) if instance['power_state'] != power_state.RUNNING: state = instance['power_state'] @@ -1473,10 +1468,10 @@ class ComputeManager(manager.SchedulerDependentManager): # Fall through and reset task_state to None current_power_state = self._get_power_state(context, instance) - self._instance_update(context, instance['uuid'], - power_state=current_power_state, - vm_state=vm_states.ACTIVE, - task_state=None) + instance = self._instance_update(context, instance['uuid'], + power_state=current_power_state, + vm_state=vm_states.ACTIVE, + task_state=None) self._notify_about_instance_usage(context, instance, "reboot.end") diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py index 1c53b6084..3e7ed1cfd 100644 --- a/nova/compute/rpcapi.py +++ b/nova/compute/rpcapi.py @@ -157,6 +157,7 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 2.21 - Add migrate_data dict param to pre_live_migration() 2.22 - Add recreate, on_shared_storage and host arguments to rebuild_instance() + 2.23 - Remove network_info from reboot_instance ''' # @@ -383,16 +384,15 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): _compute_topic(self.topic, ctxt, host, None), version='2.20') - def reboot_instance(self, ctxt, instance, - block_device_info, network_info, reboot_type): + def reboot_instance(self, ctxt, instance, block_device_info, + reboot_type): instance_p = jsonutils.to_primitive(instance) self.cast(ctxt, self.make_msg('reboot_instance', instance=instance_p, block_device_info=block_device_info, - network_info=network_info, reboot_type=reboot_type), topic=_compute_topic(self.topic, ctxt, None, instance), - version='2.5') + version='2.23') def rebuild_instance(self, ctxt, instance, new_pass, injected_files, image_ref, orig_image_ref, orig_sys_metadata, bdms, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 52e3916ae..3fdfd53c8 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -337,15 +337,6 @@ def service_get_all(context, disabled=None): @require_admin_context -def service_does_host_exist(context, host_name, include_disabled): - query = get_session().query(func.count(models.Service.host)).\ - filter_by(host=host_name) - if not include_disabled: - query = query.filter_by(disabled=False) - return query.scalar() > 0 - - -@require_admin_context def service_get_all_by_topic(context, topic): return model_query(context, models.Service, read_deleted="no").\ filter_by(disabled=False).\ diff --git a/nova/tests/api/openstack/compute/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py index be4465cf9..e103b5b19 100644 --- a/nova/tests/api/openstack/compute/contrib/test_hosts.py +++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py @@ -32,8 +32,10 @@ def stub_service_get_all(context, disabled=None): return fake_hosts.SERVICES_LIST -def stub_service_does_host_exist(context, host_name): - return host_name in [row['host'] for row in stub_service_get_all(context)] +def stub_service_get_by_host_and_topic(context, host_name, topic): + for service in stub_service_get_all(context): + if service['host'] == host_name and service['topic'] == topic: + return service def stub_set_host_enabled(context, host_name, enabled): @@ -130,8 +132,8 @@ class HostTestCase(test.TestCase): self.stubs.Set(db, 'service_get_all', stub_service_get_all) # Only hosts in our fake DB exist - self.stubs.Set(db, 'service_does_host_exist', - stub_service_does_host_exist) + self.stubs.Set(db, 'service_get_by_host_and_topic', + stub_service_get_by_host_and_topic) # 'host_c1' always succeeds, and 'host_c2' self.stubs.Set(self.hosts_api, 'set_host_enabled', stub_set_host_enabled) diff --git a/nova/tests/api/openstack/compute/contrib/test_networks.py b/nova/tests/api/openstack/compute/contrib/test_networks.py index ba65e8f6a..44d9e8af3 100644 --- a/nova/tests/api/openstack/compute/contrib/test_networks.py +++ b/nova/tests/api/openstack/compute/contrib/test_networks.py @@ -21,8 +21,8 @@ import uuid import webob -from nova.api.openstack.compute.contrib import admin_networks as networks from nova.api.openstack.compute.contrib import networks_associate +from nova.api.openstack.compute.contrib import os_networks as networks from nova import exception from nova.openstack.common import cfg from nova import test @@ -177,7 +177,7 @@ class NetworksTest(test.TestCase): def setUp(self): super(NetworksTest, self).setUp() self.fake_network_api = FakeNetworkAPI() - self.controller = networks.AdminNetworkController( + self.controller = networks.NetworkController( self.fake_network_api) self.associate_controller = networks_associate\ .NetworkAssociateActionController(self.fake_network_api) diff --git a/nova/tests/api/openstack/compute/contrib/test_services.py b/nova/tests/api/openstack/compute/contrib/test_services.py index 24f169d98..1bd47b67a 100644 --- a/nova/tests/api/openstack/compute/contrib/test_services.py +++ b/nova/tests/api/openstack/compute/contrib/test_services.py @@ -26,30 +26,30 @@ from nova.tests.api.openstack import fakes fake_services_list = [{'binary': 'nova-scheduler', 'host': 'host1', - 'availability_zone': 'nova', 'id': 1, 'disabled': True, + 'topic': 'scheduler', 'updated_at': datetime(2012, 10, 29, 13, 42, 2), 'created_at': datetime(2012, 9, 18, 2, 46, 27)}, {'binary': 'nova-compute', 'host': 'host1', - 'availability_zone': 'nova', 'id': 2, 'disabled': True, + 'topic': 'compute', 'updated_at': datetime(2012, 10, 29, 13, 42, 5), 'created_at': datetime(2012, 9, 18, 2, 46, 27)}, {'binary': 'nova-scheduler', 'host': 'host2', - 'availability_zone': 'nova', 'id': 3, 'disabled': False, + 'topic': 'scheduler', 'updated_at': datetime(2012, 9, 19, 6, 55, 34), 'created_at': datetime(2012, 9, 18, 2, 46, 28)}, {'binary': 'nova-compute', 'host': 'host2', - 'availability_zone': 'nova', 'id': 4, 'disabled': True, + 'topic': 'compute', 'updated_at': datetime(2012, 9, 18, 8, 3, 38), 'created_at': datetime(2012, 9, 18, 2, 46, 28)}, ] @@ -75,7 +75,7 @@ class FakeRequestWithHostService(object): GET = {"host": "host1", "service": "nova-compute"} -def fake_servcie_get_all(context): +def fake_service_get_all(context): return fake_services_list @@ -111,7 +111,7 @@ class ServicesTest(test.TestCase): def setUp(self): super(ServicesTest, self).setUp() - self.stubs.Set(db, "service_get_all", fake_servcie_get_all) + self.stubs.Set(db, "service_get_all", fake_service_get_all) self.stubs.Set(timeutils, "utcnow", fake_utcnow) self.stubs.Set(db, "service_get_by_args", fake_service_get_by_host_binary) @@ -128,7 +128,7 @@ class ServicesTest(test.TestCase): res_dict = self.controller.index(req) response = {'services': [{'binary': 'nova-scheduler', - 'host': 'host1', 'zone': 'nova', + 'host': 'host1', 'zone': 'internal', 'status': 'disabled', 'state': 'up', 'updated_at': datetime(2012, 10, 29, 13, 42, 2)}, {'binary': 'nova-compute', @@ -136,7 +136,7 @@ class ServicesTest(test.TestCase): 'status': 'disabled', 'state': 'up', 'updated_at': datetime(2012, 10, 29, 13, 42, 5)}, {'binary': 'nova-scheduler', 'host': 'host2', - 'zone': 'nova', + 'zone': 'internal', 'status': 'enabled', 'state': 'down', 'updated_at': datetime(2012, 9, 19, 6, 55, 34)}, {'binary': 'nova-compute', 'host': 'host2', @@ -150,7 +150,7 @@ class ServicesTest(test.TestCase): res_dict = self.controller.index(req) response = {'services': [{'binary': 'nova-scheduler', 'host': 'host1', - 'zone': 'nova', + 'zone': 'internal', 'status': 'disabled', 'state': 'up', 'updated_at': datetime(2012, 10, 29, 13, 42, 2)}, {'binary': 'nova-compute', 'host': 'host1', diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index e3810510b..485968209 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -185,7 +185,6 @@ class ExtensionControllerTest(ExtensionTestCase): "Keypairs", "Multinic", "MultipleCreate", - "OSNetworks", "QuotaClasses", "Quotas", "Rescue", diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 40bc8d148..0d9f67231 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -1000,8 +1000,7 @@ class ComputeTestCase(BaseTestCase): # This is a true unit test, so we don't need the network stubs. fake_network.unset_stub_network_methods(self.stubs) - self.mox.StubOutWithMock(network_model.NetworkInfo, - 'hydrate') + self.mox.StubOutWithMock(self.compute, '_get_instance_nw_info') self.mox.StubOutWithMock(self.compute, '_notify_about_instance_usage') self.mox.StubOutWithMock(self.compute, '_instance_update') self.mox.StubOutWithMock(self.compute, '_get_power_state') @@ -1010,8 +1009,11 @@ class ComputeTestCase(BaseTestCase): instance = dict(uuid='fake-instance', power_state='unknown') + updated_instance1 = dict(uuid='updated-instance1', + power_state='fake') + updated_instance2 = dict(uuid='updated-instance2', + power_state='fake') - fake_nw_info = 'fake-network-info' fake_nw_model = network_model.NetworkInfo() self.mox.StubOutWithMock(fake_nw_model, 'legacy') @@ -1031,8 +1033,9 @@ class ComputeTestCase(BaseTestCase): self.mox.StubOutWithMock(self.context, 'elevated') self.context.elevated().AndReturn(econtext) - network_model.NetworkInfo.hydrate(fake_nw_info).AndReturn( - fake_nw_model) + self.compute._get_instance_nw_info(econtext, + instance).AndReturn( + fake_nw_model) self.compute._notify_about_instance_usage(econtext, instance, 'reboot.start') @@ -1040,7 +1043,7 @@ class ComputeTestCase(BaseTestCase): instance).AndReturn(fake_power_state1) self.compute._instance_update(econtext, instance['uuid'], power_state=fake_power_state1, - vm_state=vm_states.ACTIVE) + vm_state=vm_states.ACTIVE).AndReturn(updated_instance1) # Reboot should check the driver to see if legacy nwinfo is # needed. If it is, the model's legacy() method should be @@ -1058,7 +1061,7 @@ class ComputeTestCase(BaseTestCase): # this is called with the wrong args, so we have to hack # around it. reboot_call_info = {} - expected_call_info = {'args': (instance, expected_nw_info, + expected_call_info = {'args': (updated_instance1, expected_nw_info, reboot_type, fake_block_dev_info), 'kwargs': {}} @@ -1070,19 +1073,18 @@ class ComputeTestCase(BaseTestCase): # Power state should be updated again self.compute._get_power_state(econtext, - instance).AndReturn(fake_power_state2) - self.compute._instance_update(econtext, instance['uuid'], + updated_instance1).AndReturn(fake_power_state2) + self.compute._instance_update(econtext, updated_instance1['uuid'], power_state=fake_power_state2, task_state=None, - vm_state=vm_states.ACTIVE) + vm_state=vm_states.ACTIVE).AndReturn(updated_instance2) self.compute._notify_about_instance_usage(econtext, - instance, + updated_instance2, 'reboot.end') self.mox.ReplayAll() self.compute.reboot_instance(self.context, instance=instance, block_device_info=fake_block_dev_info, - network_info=fake_nw_info, reboot_type=reboot_type) self.assertEqual(expected_call_info, reboot_call_info) @@ -4233,12 +4235,10 @@ class ComputeAPITestCase(BaseTestCase): def _stub_out_reboot(self, device_name): def fake_reboot_instance(rpcapi, context, instance, block_device_info, - network_info, reboot_type): self.assertEqual( block_device_info['block_device_mapping'][0]['mount_device'], device_name) - self.assertEqual(network_info[0]['network']['bridge'], 'fake_br1') self.stubs.Set(nova.compute.rpcapi.ComputeAPI, 'reboot_instance', fake_reboot_instance) @@ -6151,81 +6151,6 @@ class ComputePolicyTestCase(BaseTestCase): availability_zone='1:1') -class ComputeHostAPITestCase(BaseTestCase): - def setUp(self): - super(ComputeHostAPITestCase, self).setUp() - self.host_api = compute_api.HostAPI() - - def _rpc_call_stub(self, call_info): - def fake_rpc_call(context, topic, msg, timeout=None): - call_info['context'] = context - call_info['topic'] = topic - call_info['msg'] = msg - self.stubs.Set(rpc, 'call', fake_rpc_call) - - def _pretend_fake_host_exists(self, ctxt): - """Sets it so that the host API always thinks that 'fake_host' - exists""" - self.mox.StubOutWithMock(self.host_api, 'does_host_exist') - self.host_api.does_host_exist(ctxt, 'fake_host').AndReturn(True) - self.mox.ReplayAll() - - def test_set_host_enabled(self): - ctxt = context.get_admin_context() - call_info = {} - self._rpc_call_stub(call_info) - - self._pretend_fake_host_exists(ctxt) - self.host_api.set_host_enabled(ctxt, 'fake_host', 'fake_enabled') - self.assertEqual(call_info['context'], ctxt) - self.assertEqual(call_info['topic'], 'compute.fake_host') - self.assertEqual(call_info['msg'], - {'method': 'set_host_enabled', - 'args': {'enabled': 'fake_enabled'}, - 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) - - def test_get_host_uptime(self): - ctxt = context.RequestContext('fake', 'fake') - call_info = {} - self._rpc_call_stub(call_info) - - self._pretend_fake_host_exists(ctxt) - self.host_api.get_host_uptime(ctxt, 'fake_host') - self.assertEqual(call_info['context'], ctxt) - self.assertEqual(call_info['topic'], 'compute.fake_host') - self.assertEqual(call_info['msg'], - {'method': 'get_host_uptime', - 'args': {}, - 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) - - def test_host_power_action(self): - ctxt = context.get_admin_context() - call_info = {} - self._rpc_call_stub(call_info) - self._pretend_fake_host_exists(ctxt) - self.host_api.host_power_action(ctxt, 'fake_host', 'fake_action') - self.assertEqual(call_info['context'], ctxt) - self.assertEqual(call_info['topic'], 'compute.fake_host') - self.assertEqual(call_info['msg'], - {'method': 'host_power_action', - 'args': {'action': 'fake_action'}, - 'version': - compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) - - def test_set_host_maintenance(self): - ctxt = context.get_admin_context() - call_info = {} - self._rpc_call_stub(call_info) - self._pretend_fake_host_exists(ctxt) - self.host_api.set_host_maintenance(ctxt, 'fake_host', 'fake_mode') - self.assertEqual(call_info['context'], ctxt) - self.assertEqual(call_info['topic'], 'compute.fake_host') - self.assertEqual(call_info['msg'], - {'method': 'host_maintenance_mode', - 'args': {'host': 'fake_host', 'mode': 'fake_mode'}, - 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) - - class KeypairAPITestCase(BaseTestCase): def setUp(self): super(KeypairAPITestCase, self).setUp() diff --git a/nova/tests/compute/test_host_api.py b/nova/tests/compute/test_host_api.py index 0af1d6766..95d3c4926 100644 --- a/nova/tests/compute/test_host_api.py +++ b/nova/tests/compute/test_host_api.py @@ -13,93 +13,114 @@ # License for the specific language governing permissions and limitations # under the License. -from nova.compute import api +from nova import compute +from nova.compute import rpcapi as compute_rpcapi from nova import context -from nova import db -from nova import exception +from nova.openstack.common import rpc from nova import test -from nova.tests import fake_hosts -class HostApiTestCase(test.TestCase): - """ - Tests 'host' subset of the compute api - """ - +class ComputeHostAPITestCase(test.TestCase): def setUp(self): - super(HostApiTestCase, self).setUp() - self.compute_rpcapi = api.compute_rpcapi - self.api = api.HostAPI() + super(ComputeHostAPITestCase, self).setUp() + self.host_api = compute.HostAPI() + self.ctxt = context.get_admin_context() - def test_bad_host_set_enabled(self): - """ - Tests that actions on single hosts that don't exist blow up without - having to reach the host via rpc. Should raise HostNotFound if you - try to update a host that is not in the DB + def _mock_rpc_call(self, expected_message, result=None): + if result is None: + result = 'fake-result' + self.mox.StubOutWithMock(rpc, 'call') + rpc.call(self.ctxt, 'compute.fake_host', + expected_message, None).AndReturn(result) + + def _mock_assert_host_exists(self): + """Sets it so that the host API always thinks that 'fake_host' + exists. """ - self.assertRaises(exception.HostNotFound, self.api.set_host_enabled, - context.get_admin_context(), "bogus_host_name", False) + self.mox.StubOutWithMock(self.host_api, '_assert_host_exists') + self.host_api._assert_host_exists(self.ctxt, 'fake_host') + + def test_set_host_enabled(self): + self._mock_assert_host_exists() + self._mock_rpc_call( + {'method': 'set_host_enabled', + 'args': {'enabled': 'fake_enabled'}, + 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) + + self.mox.ReplayAll() + result = self.host_api.set_host_enabled(self.ctxt, 'fake_host', + 'fake_enabled') + self.assertEqual('fake-result', result) + + def test_get_host_uptime(self): + self._mock_assert_host_exists() + self._mock_rpc_call( + {'method': 'get_host_uptime', + 'args': {}, + 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) + self.mox.ReplayAll() + result = self.host_api.get_host_uptime(self.ctxt, 'fake_host') + self.assertEqual('fake-result', result) + + def test_host_power_action(self): + self._mock_assert_host_exists() + self._mock_rpc_call( + {'method': 'host_power_action', + 'args': {'action': 'fake_action'}, + 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) + self.mox.ReplayAll() + result = self.host_api.host_power_action(self.ctxt, 'fake_host', + 'fake_action') + self.assertEqual('fake-result', result) - def test_list_compute_hosts(self): - ctx = context.get_admin_context() - self.mox.StubOutWithMock(db, 'service_get_all') - db.service_get_all(ctx, False).AndReturn(fake_hosts.SERVICES_LIST) + def test_set_host_maintenance(self): + self._mock_assert_host_exists() + self._mock_rpc_call( + {'method': 'host_maintenance_mode', + 'args': {'host': 'fake_host', 'mode': 'fake_mode'}, + 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION}) self.mox.ReplayAll() - compute_hosts = self.api.list_hosts(ctx, service="compute") + result = self.host_api.set_host_maintenance(self.ctxt, 'fake_host', + 'fake_mode') + self.assertEqual('fake-result', result) + + def test_service_get_all(self): + services = [dict(id=1, key1='val1', key2='val2', topic='compute', + host='host1'), + dict(id=2, key1='val2', key3='val3', topic='compute', + host='host2')] + exp_services = [] + for service in services: + exp_service = {} + exp_service.update(availability_zone='nova', **service) + exp_services.append(exp_service) + + self.mox.StubOutWithMock(self.host_api.db, + 'service_get_all') + + # Test no filters + self.host_api.db.service_get_all(self.ctxt, False).AndReturn( + services) + self.mox.ReplayAll() + result = self.host_api.service_get_all(self.ctxt) self.mox.VerifyAll() - expected = [host for host in fake_hosts.HOST_LIST - if host["service"] == "compute"] - self.assertEqual(expected, compute_hosts) + self.assertEqual(exp_services, result) - def test_describe_host(self): - """ - Makes sure that describe_host returns the correct information - given our fake input. - """ - ctx = context.get_admin_context() - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - host_name = 'host_c1' - db.service_get_by_compute_host(ctx, host_name).AndReturn( - {'host': 'fake_host', - 'compute_node': [ - {'vcpus': 4, - 'vcpus_used': 1, - 'memory_mb': 8192, - 'memory_mb_used': 2048, - 'local_gb': 1024, - 'local_gb_used': 648} - ] - }) - self.mox.StubOutWithMock(db, 'instance_get_all_by_host') - db.instance_get_all_by_host(ctx, 'fake_host').AndReturn( - [{'project_id': 42, - 'vcpus': 1, - 'memory_mb': 2048, - 'root_gb': 648, - 'ephemeral_gb': 0, - }]) + # Test no filters #2 + self.mox.ResetAll() + self.host_api.db.service_get_all(self.ctxt, False).AndReturn( + services) + self.mox.ReplayAll() + result = self.host_api.service_get_all(self.ctxt, filters={}) + self.mox.VerifyAll() + self.assertEqual(exp_services, result) + + # Test w/ filter + self.mox.ResetAll() + self.host_api.db.service_get_all(self.ctxt, False).AndReturn( + services) self.mox.ReplayAll() - result = self.api.describe_host(ctx, host_name) - self.assertEqual(result, - [{'resource': {'cpu': 4, - 'disk_gb': 1024, - 'host': 'host_c1', - 'memory_mb': 8192, - 'project': '(total)'}}, - {'resource': {'cpu': 1, - 'disk_gb': 648, - 'host': 'host_c1', - 'memory_mb': 2048, - 'project': '(used_now)'}}, - {'resource': {'cpu': 1, - 'disk_gb': 648, - 'host': 'host_c1', - 'memory_mb': 2048, - 'project': '(used_max)'}}, - {'resource': {'cpu': 1, - 'disk_gb': 648, - 'host': 'host_c1', - 'memory_mb': 2048, - 'project': 42}}] - ) + result = self.host_api.service_get_all(self.ctxt, + filters=dict(key1='val2')) self.mox.VerifyAll() + self.assertEqual([exp_services[1]], result) diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py index a31d9a14b..00b90ea65 100644 --- a/nova/tests/compute/test_rpcapi.py +++ b/nova/tests/compute/test_rpcapi.py @@ -236,9 +236,8 @@ class ComputeRpcAPITestCase(test.TestCase): self._test_compute_api('reboot_instance', 'cast', instance=self.fake_instance, block_device_info={}, - network_info={}, reboot_type='type', - version='2.5') + version='2.23') def test_rebuild_instance(self): self._test_compute_api('rebuild_instance', 'cast', diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index c5d160209..51f3a3f85 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -136,10 +136,10 @@ policy_data = """ "compute_extension:instance_usage_audit_log": "", "compute_extension:keypairs": "", "compute_extension:multinic": "", - "compute_extension:admin_networks": "", - "compute_extension:admin_networks:view": "", + "compute_extension:networks": "", + "compute_extension:networks:view": "", "compute_extension:networks_associate": "", - "compute_extension:os-networks": "", + "compute_extension:os-tenant-networks": "", "compute_extension:quotas:show": "", "compute_extension:quotas:update": "", "compute_extension:quota_classes": "", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index 0dd777fe2..3d69fad45 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -305,19 +305,19 @@ "updated": "%(timestamp)s" }, { - "alias": "os-admin-networks", + "alias": "os-networks", "description": "%(text)s", "links": [], - "name": "AdminNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1", + "name": "Networks", + "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", "updated": "%(timestamp)s" }, { - "alias": "os-networks", + "alias": "os-tenant-networks", "description": "%(text)s", "links": [], - "name": "OSNetworks", - "namespace": "http://docs.openstack.org/compute/ext/os-networks/api/v1.1", + "name": "OSTenantNetworks", + "namespace": "http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2", "updated": "%(timestamp)s" }, { diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index fe34f369b..5953ba704 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -114,10 +114,10 @@ <extension alias="os-multiple-create" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/multiplecreate/api/v1.1" name="MultipleCreate"> <description>%(text)s</description> </extension> - <extension alias="os-admin-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-admin-networks/api/v1.1" name="AdminNetworks"> + <extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="Networks"> <description>%(text)s</description> </extension> - <extension alias="os-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-networks/api/v1.1" name="OSNetworks"> + <extension alias="os-tenant-networks" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/os-tenant-networks/api/v2" name="OSTenantNetworks"> <description>%(text)s</description> </extension> <extension alias="os-networks-associate" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport"> diff --git a/nova/tests/integrated/api_samples/os-networks/networks-list-res.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-list-res.json.tpl index 757084d2f..757084d2f 100644 --- a/nova/tests/integrated/api_samples/os-networks/networks-list-res.json.tpl +++ b/nova/tests/integrated/api_samples/os-tenant-networks/networks-list-res.json.tpl diff --git a/nova/tests/integrated/api_samples/os-networks/networks-post-req.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-req.json.tpl index fb1c2d3d0..fb1c2d3d0 100644 --- a/nova/tests/integrated/api_samples/os-networks/networks-post-req.json.tpl +++ b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-req.json.tpl diff --git a/nova/tests/integrated/api_samples/os-networks/networks-post-res.json.tpl b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-res.json.tpl index ff9e2273d..ff9e2273d 100644 --- a/nova/tests/integrated/api_samples/os-networks/networks-post-res.json.tpl +++ b/nova/tests/integrated/api_samples/os-tenant-networks/networks-post-res.json.tpl diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 0fb0e9107..98ac6a230 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -371,7 +371,7 @@ class ApiSamplesTrap(ApiSampleTestBase): do_not_approve_additions.append('os-fping') do_not_approve_additions.append('os-hypervisors') do_not_approve_additions.append('os-instance_usage_audit_log') - do_not_approve_additions.append('os-admin-networks') + do_not_approve_additions.append('os-networks') do_not_approve_additions.append('os-services') do_not_approve_additions.append('os-volumes') @@ -2359,8 +2359,8 @@ class DiskConfigXmlTest(DiskConfigJsonTest): class OsNetworksJsonTests(ApiSampleTestBase): - extension_name = ("nova.api.openstack.compute.contrib.os_networks" - ".Os_networks") + extension_name = ("nova.api.openstack.compute.contrib.os_tenant_networks" + ".Os_tenant_networks") def setUp(self): super(OsNetworksJsonTests, self).setUp() @@ -2377,21 +2377,22 @@ class OsNetworksJsonTests(ApiSampleTestBase): self.stubs.Set(nova.quota.QuotaEngine, "rollback", fake) def test_list_networks(self): - response = self._do_get('os-networks') + response = self._do_get('os-tenant-networks') self.assertEqual(response.status, 200) subs = self._get_regexes() return self._verify_response('networks-list-res', subs, response) def test_create_network(self): - response = self._do_post('os-networks', "networks-post-req", {}) + response = self._do_post('os-tenant-networks', "networks-post-req", {}) self.assertEqual(response.status, 200) subs = self._get_regexes() self._verify_response('networks-post-res', subs, response) - def test_delete_networK(self): - response = self._do_post('os-networks', "networks-post-req", {}) + def test_delete_network(self): + response = self._do_post('os-tenant-networks', "networks-post-req", {}) net = json.loads(response.read()) - response = self._do_delete('os-networks/%s' % net["network"]["id"]) + response = self._do_delete('os-tenant-networks/%s' % + net["network"]["id"]) self.assertEqual(response.status, 202) @@ -2406,7 +2407,7 @@ class NetworksAssociateJsonTests(ApiSampleTestBase): f['osapi_compute_extension'] = CONF.osapi_compute_extension[:] # Networks_associate requires Networks to be update f['osapi_compute_extension'].append( - 'nova.api.openstack.compute.contrib.admin_networks.Admin_networks') + 'nova.api.openstack.compute.contrib.os_networks.Os_networks') return f def setUp(self): @@ -2420,25 +2421,25 @@ class NetworksAssociateJsonTests(ApiSampleTestBase): self.stubs.Set(network_api.API, "associate", fake_associate) def test_disassociate(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-req', {}) self.assertEqual(response.status, 202) def test_disassociate_host(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-host-req', {}) self.assertEqual(response.status, 202) def test_disassociate_project(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-disassociate-project-req', {}) self.assertEqual(response.status, 202) def test_associate_host(self): - response = self._do_post('os-admin-networks/1/action', + response = self._do_post('os-networks/1/action', 'network-associate-host-req', {"host": "testHost"}) self.assertEqual(response.status, 202) diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py index 29e63aba7..f15d71633 100644 --- a/nova/tests/test_metadata.py +++ b/nova/tests/test_metadata.py @@ -342,7 +342,7 @@ class OpenStackMetadataTestCase(test.TestCase): mdinst = fake_InstanceMetadata(self.stubs, inst) # since this instance had no user-data it should not be there. - self.assertFalse('user-data' in mdinst.lookup("/openstack/2012-08-10")) + self.assertFalse('user_data' in mdinst.lookup("/openstack/2012-08-10")) self.assertRaises(base.InvalidMetadataPath, mdinst.lookup, "/openstack/2012-08-10/user_data") @@ -362,6 +362,14 @@ class OpenStackMetadataTestCase(test.TestCase): mdjson = mdinst.lookup("/openstack/2012-08-10/meta_data.json") self.assertFalse("random_seed" in json.loads(mdjson)) + def test_no_dashes_in_metadata(self): + # top level entries in meta_data should not contain '-' in their name + inst = copy(self.instance) + mdinst = fake_InstanceMetadata(self.stubs, inst) + mdjson = json.loads(mdinst.lookup("/openstack/latest/meta_data.json")) + + self.assertEqual([], [k for k in mdjson.keys() if k.find("-") != -1]) + class MetadataHandlerTestCase(test.TestCase): """Test that metadata is returning proper values.""" |