diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-08-12 02:15:37 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-08-12 02:15:37 +0000 |
| commit | 480f4e2a3a2992acf25628eddfc6f87379cf2445 (patch) | |
| tree | 119af3f3c3d86d04346fa6e5444291a1783b6f6a /nova | |
| parent | 0effb5ce1dec1ebb0dfc748ac7f67d771c1281a7 (diff) | |
| parent | 357ffa7d3aa2124d9d7697a4bd492b7819646ba9 (diff) | |
| download | nova-480f4e2a3a2992acf25628eddfc6f87379cf2445.tar.gz nova-480f4e2a3a2992acf25628eddfc6f87379cf2445.tar.xz nova-480f4e2a3a2992acf25628eddfc6f87379cf2445.zip | |
Merge "remove nova code related to Quantum v1 API"
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/flags.py | 2 | ||||
| -rw-r--r-- | nova/network/manager.py | 2 | ||||
| -rw-r--r-- | nova/network/nova_ipam_lib.py | 101 | ||||
| -rw-r--r-- | nova/network/quantum/__init__.py | 16 | ||||
| -rw-r--r-- | nova/network/quantum/client.py | 312 | ||||
| -rw-r--r-- | nova/network/quantum/fake_client.py | 171 | ||||
| -rw-r--r-- | nova/network/quantum/manager.py | 762 | ||||
| -rw-r--r-- | nova/network/quantum/melange_connection.py | 202 | ||||
| -rw-r--r-- | nova/network/quantum/melange_ipam_lib.py | 261 | ||||
| -rw-r--r-- | nova/network/quantum/quantum_connection.py | 159 | ||||
| -rw-r--r-- | nova/network/sg.py (renamed from nova/network/quantum/sg.py) | 0 | ||||
| -rw-r--r-- | nova/tests/fake_network.py | 2 | ||||
| -rw-r--r-- | nova/tests/network/test_quantum.py | 647 | ||||
| -rw-r--r-- | nova/tests/test_melange_ipam_lib.py | 243 |
14 files changed, 104 insertions, 2776 deletions
diff --git a/nova/flags.py b/nova/flags.py index 588ecfe5f..4960e9c35 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -410,7 +410,7 @@ global_opts = [ default='nova.volume.api.API', help='The full class name of the volume API class to use'), cfg.StrOpt('security_group_handler', - default='nova.network.quantum.sg.NullSecurityGroupHandler', + default='nova.network.sg.NullSecurityGroupHandler', help='The full class name of the security group handler class'), cfg.StrOpt('default_access_ip_network_name', default=None, diff --git a/nova/network/manager.py b/nova/network/manager.py index 116413007..a79700dcd 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -789,7 +789,7 @@ class NetworkManager(manager.SchedulerDependentManager): # NOTE(tr3buchet: unless manager subclassing NetworkManager has # already imported ipam, import nova ipam here if not hasattr(self, 'ipam'): - self._import_ipam_lib('nova.network.quantum.nova_ipam_lib') + self._import_ipam_lib('nova.network.nova_ipam_lib') l3_lib = kwargs.get("l3_lib", FLAGS.l3_lib) self.l3driver = importutils.import_object(l3_lib) diff --git a/nova/network/nova_ipam_lib.py b/nova/network/nova_ipam_lib.py new file mode 100644 index 000000000..be8858983 --- /dev/null +++ b/nova/network/nova_ipam_lib.py @@ -0,0 +1,101 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks, Inc +# 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. + +from nova import db +from nova import exception +from nova import ipv6 +from nova.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) + + +def get_ipam_lib(net_man): + return QuantumNovaIPAMLib(net_man) + + +class QuantumNovaIPAMLib(object): + """Implements Quantum IP Address Management (IPAM) interface + using the local Nova database. This implementation is inline + with how IPAM is used by other NetworkManagers. + """ + + def __init__(self, net_manager): + """Holds a reference to the "parent" network manager, used + to take advantage of various FlatManager methods to avoid + code duplication. + """ + self.net_manager = net_manager + + def get_subnets_by_net_id(self, context, tenant_id, net_id, _vif_id=None): + """Returns information about the IPv4 and IPv6 subnets + associated with a Quantum Network UUID. + """ + n = db.network_get_by_uuid(context.elevated(), net_id) + subnet_v4 = { + 'network_id': n['uuid'], + 'cidr': n['cidr'], + 'gateway': n['gateway'], + 'broadcast': n['broadcast'], + 'netmask': n['netmask'], + 'version': 4, + 'dns1': n['dns1'], + 'dns2': n['dns2']} + #TODO(tr3buchet): I'm noticing we've assumed here that all dns is v4. + # this is probably bad as there is no way to add v6 + # dns to nova + subnet_v6 = { + 'network_id': n['uuid'], + 'cidr': n['cidr_v6'], + 'gateway': n['gateway_v6'], + 'broadcast': None, + 'netmask': n['netmask_v6'], + 'version': 6, + 'dns1': None, + 'dns2': None} + return [subnet_v4, subnet_v6] + + def get_routes_by_ip_block(self, context, block_id, project_id): + """Returns the list of routes for the IP block""" + return [] + + def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): + """Returns a list of IPv4 address strings associated with + the specified virtual interface, based on the fixed_ips table. + """ + # TODO(tr3buchet): link fixed_ips to vif by uuid so only 1 db call + vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) + fixed_ips = db.fixed_ips_by_virtual_interface(context, + vif_rec['id']) + return [fixed_ip['address'] for fixed_ip in fixed_ips] + + def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): + """Returns a list containing a single IPv6 address strings + associated with the specified virtual interface. + """ + admin_context = context.elevated() + network = db.network_get_by_uuid(admin_context, net_id) + vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) + if network['cidr_v6']: + ip = ipv6.to_global(network['cidr_v6'], + vif_rec['address'], + project_id) + return [ip] + return [] + + def get_floating_ips_by_fixed_address(self, context, fixed_address): + return db.floating_ip_get_by_fixed_address(context, fixed_address) diff --git a/nova/network/quantum/__init__.py b/nova/network/quantum/__init__.py deleted file mode 100644 index f7fbfb511..000000000 --- a/nova/network/quantum/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Nicira Networks -# 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. diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py deleted file mode 100644 index 538a0b3a0..000000000 --- a/nova/network/quantum/client.py +++ /dev/null @@ -1,312 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Citrix Systems -# 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. -# @author: Tyler Smith, Cisco Systems - -import httplib -import socket -import urllib - -from nova.openstack.common import jsonutils - - -# FIXME(danwent): All content in this file should be removed once the -# packaging work for the quantum client libraries is complete. -# At that point, we will be able to just install the libraries as a -# dependency and import from quantum.client.* and quantum.common.* -# Until then, we have simplified versions of these classes in this file. - -class JSONSerializer(object): - """This is a simple json-only serializer to use until we can just grab - the standard serializer from the quantum library. - """ - def serialize(self, data, content_type): - return jsonutils.dumps(data) - - def deserialize(self, data, content_type): - return jsonutils.loads(data) - - -# Quantum API v1.0 uses 420 + 430 for network + port not found -# Quantum API v1.1 uses 404 for network + port not found -NOT_FOUND_CODES = set((404, 420, 430)) - - -# The full client lib will expose more -# granular exceptions, for now, just try to distinguish -# between the cases we care about. -class QuantumNotFoundException(Exception): - """Indicates that Quantum Server returned a not-found error code""" - pass - - -class QuantumServerException(Exception): - """Indicates any non-404 error from Quantum Server""" - pass - - -class QuantumIOException(Exception): - """Indicates network IO trouble reaching Quantum Server""" - pass - - -class api_call(object): - """A Decorator to add support for format and tenant overriding""" - def __init__(self, func): - self.func = func - - def __get__(self, instance, owner): - def with_params(*args, **kwargs): - """Temporarily set format and tenant for this request""" - (format, tenant) = (instance.format, instance.tenant) - - instance.format = kwargs.pop('format', instance.format) - instance.tenant = kwargs.pop('tenant', instance.tenant) - - ret = None - try: - ret = self.func(instance, *args, **kwargs) - finally: - (instance.format, instance.tenant) = (format, tenant) - return ret - return with_params - - -class Client(object): - """A base client class - derived from Glance.BaseClient""" - - action_prefix = '/v1.1/tenants/{tenant_id}' - - """Action query strings""" - networks_path = "/networks" - network_path = "/networks/%s" - ports_path = "/networks/%s/ports" - port_path = "/networks/%s/ports/%s" - attachment_path = "/networks/%s/ports/%s/attachment" - - def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, - format="xml", testing_stub=None, key_file=None, - cert_file=None, logger=None, timeout=None): - """Creates a new client to some service. - - :param host: The host where service resides - :param port: The port where service resides - :param use_ssl: True to use SSL, False to use HTTP - :param tenant: The tenant ID to make requests with - :param format: The format to query the server with - :param testing_stub: A class that stubs basic server methods for tests - :param key_file: The SSL key file to use if use_ssl is true - :param cert_file: The SSL cert file to use if use_ssl is true - :param logger: logging object to be used by client library - """ - self.host = host - self.port = port - self.use_ssl = use_ssl - self.tenant = tenant - self.format = format - self.connection = None - self.testing_stub = testing_stub - self.key_file = key_file - self.cert_file = cert_file - self.logger = logger - self.timeout = timeout - - def get_connection_type(self): - """Returns the proper connection type""" - if self.testing_stub: - return self.testing_stub - elif self.use_ssl: - return httplib.HTTPSConnection - else: - return httplib.HTTPConnection - - def do_request(self, method, action, body=None, - headers=None, params=None): - """Connects to the server and issues a request. - Returns the result data, or raises an appropriate exception if - HTTP status code is not 2xx - - :param method: HTTP method ("GET", "POST", "PUT", etc...) - :param body: string of data to send, or None (default) - :param headers: mapping of key/value pairs to add as headers - :param params: dictionary of key/value pairs to add to append - to action - """ - - # Ensure we have a tenant id - if not self.tenant: - raise Exception(_("Tenant ID not set")) - - # Add format and tenant_id - action += ".%s" % self.format - action = Client.action_prefix + action - action = action.replace('{tenant_id}', self.tenant) - - if isinstance(params, dict): - action += '?' + urllib.urlencode(params) - - try: - connection_type = self.get_connection_type() - headers = headers or {"Content-Type": - "application/%s" % self.format} - - # Open connection and send request, handling SSL certs - kwargs = {} - if self.use_ssl: - if self.key_file: - kwargs['key_file'] = self.key_file - if self.cert_file: - kwargs['cert_file'] = self.cert_file - - if self.timeout: - kwargs['timeout'] = self.timeout - - c = connection_type(self.host, self.port, **kwargs) - - if self.logger: - self.logger.debug( - _("Quantum Client Request: %(method)s %(action)s"), - locals()) - if body: - self.logger.debug(body) - - c.request(method, action, body, headers) - res = c.getresponse() - status_code = self.get_status_code(res) - data = res.read() - - if self.logger: - self.logger.debug("Quantum Client Reply (code = %s) :\n %s" % - (str(status_code), data)) - - if status_code in NOT_FOUND_CODES: - raise QuantumNotFoundException( - _("Quantum entity not found: %s"), data) - - if status_code in (httplib.OK, - httplib.CREATED, - httplib.ACCEPTED, - httplib.NO_CONTENT): - if data is not None and len(data): - return self.deserialize(data, status_code) - else: - raise QuantumServerException( - _("Server %(status_code)s error: %(data)s") - % locals()) - - except (socket.error, IOError), e: - raise QuantumIOException(_("Unable to connect to " - "server. Got error: %s") % e) - - def get_status_code(self, response): - """Returns the integer status code from the response, which - can be either a Webob.Response (used in testing) or httplib.Response - """ - if hasattr(response, 'status_int'): - return response.status_int - else: - return response.status - - def serialize(self, data): - if not data: - return None - elif isinstance(data, dict): - return JSONSerializer().serialize(data, self.content_type()) - else: - raise Exception(_("unable to deserialize object of type = '%s'") % - type(data)) - - def deserialize(self, data, status_code): - return JSONSerializer().deserialize(data, self.content_type()) - - def content_type(self, format=None): - if not format: - format = self.format - return "application/%s" % (format) - - @api_call - def list_networks(self, filter_ops=None): - """Fetches a list of all networks for a tenant""" - return self.do_request("GET", self.networks_path, params=filter_ops) - - @api_call - def show_network_details(self, network): - """Fetches the details of a certain network""" - return self.do_request("GET", self.network_path % (network)) - - @api_call - def create_network(self, body=None): - """Creates a new network""" - body = self.serialize(body) - return self.do_request("POST", self.networks_path, body=body) - - @api_call - def update_network(self, network, body=None): - """Updates a network""" - body = self.serialize(body) - return self.do_request("PUT", self.network_path % (network), body=body) - - @api_call - def delete_network(self, network): - """Deletes the specified network""" - return self.do_request("DELETE", self.network_path % (network)) - - @api_call - def list_ports(self, network, filter_ops=None): - """Fetches a list of ports on a given network""" - return self.do_request("GET", self.ports_path % (network), - params=filter_ops) - - @api_call - def show_port_details(self, network, port): - """Fetches the details of a certain port""" - return self.do_request("GET", self.port_path % (network, port)) - - @api_call - def create_port(self, network, body=None): - """Creates a new port on a given network""" - body = self.serialize(body) - return self.do_request("POST", self.ports_path % (network), body=body) - - @api_call - def delete_port(self, network, port): - """Deletes the specified port from a network""" - return self.do_request("DELETE", self.port_path % (network, port)) - - @api_call - def set_port_state(self, network, port, body=None): - """Sets the state of the specified port""" - body = self.serialize(body) - return self.do_request("PUT", - self.port_path % (network, port), body=body) - - @api_call - def show_port_attachment(self, network, port): - """Fetches the attachment-id associated with the specified port""" - return self.do_request("GET", self.attachment_path % (network, port)) - - @api_call - def attach_resource(self, network, port, body=None): - """Sets the attachment-id of the specified port""" - body = self.serialize(body) - return self.do_request("PUT", - self.attachment_path % (network, port), body=body) - - @api_call - def detach_resource(self, network, port): - """Removes the attachment-id of the specified port""" - return self.do_request("DELETE", - self.attachment_path % (network, port)) diff --git a/nova/network/quantum/fake_client.py b/nova/network/quantum/fake_client.py deleted file mode 100644 index 2e0231e61..000000000 --- a/nova/network/quantum/fake_client.py +++ /dev/null @@ -1,171 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 Nicira Networks -# 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. -# @author: Dan Wendlandt Nicira Networks - -from nova.network.quantum import client -from nova import utils - -#TODO(danwent): would be nice to have these functions raise QuantumIOErrors -# to make sure such errors are caught and reported properly - - -# this is a fake quantum client that just stores all data in a nested dict -# -# example: -# -#{'<tenant>': -# { '<net-uuid>' : { 'name' : "t1-net1", -# 'ports' : [ { '<port-uuid'> : -# {'state': 'Active', -# 'attachment': 'iface-id'}, -# { 'state': 'Down', -# 'attachment' : None}}] -# } -# } -# } - - -class FakeClient(object): - """A fake Quantum Client for testing""" - - def __init__(self, logger=None, **kwargs): - """Creates a new client to some service. - :param logger: logging object to be used by client library - """ - self.logger = logger - self.tenant_map = {} - - def _get_tenant_nets(self, tenant): - if tenant is None: - raise Exception("'tenant' cannot be None") - return self.tenant_map.setdefault(tenant, {}) - - def _verify_net(self, network, nets): - if network not in nets: - raise client.QuantumNotFoundException("no network with uuid %s" - % network) - - def _verify_net_and_port(self, network, port, nets): - if network not in nets: - raise client.QuantumNotFoundException("no network with uuid %s" - % network) - if port not in nets[network]['ports']: - raise client.QuantumNotFoundException("no port with uuid %s" - % port) - - def list_networks(self, tenant=None, filter_ops=None): - """Fetches a list of all networks for a tenant""" - nets = self._get_tenant_nets(tenant) - if filter_ops: - raise Exception("Need to implement filters %s in " - "quantum fake client" % filter_ops) - return {"networks": [{"id": uuid} for uuid in nets.keys()]} - - def show_network_details(self, network, tenant=None): - """Fetches the details of a certain network""" - nets = self._get_tenant_nets(tenant) - self._verify_net(network, nets) - return {"network": {"id": network, "name": nets[network]["name"]}} - - def create_network(self, body=None, tenant=None): - """Creates a new network""" - nets = self._get_tenant_nets(tenant) - uuid = str(utils.gen_uuid()) - name = body["network"]["name"] - nets[uuid] = {"ports": {}, "name": name} - return {"network": {"id": uuid}} - - def update_network(self, network, body=None, tenant=None): - """Updates a network""" - nets = self._get_tenant_nets(tenant) - self._verify_net(network, nets) - nets[network]['name'] = body["network"]["name"] - return {"network": {"id": network, "name": nets[networks]["name"]}} - - def delete_network(self, network, tenant=None): - """Deletes the specified network""" - nets = self._get_tenant_nets(tenant) - self._verify_net(network, nets) - del nets[network] - - def list_ports(self, network, filter_ops=None, tenant=None): - """Fetches a list of ports on a given network""" - nets = self._get_tenant_nets(tenant) - self._verify_net(network, nets) - - ports = nets[network]['ports'].items() - if filter_ops and 'attachment' in filter_ops: - a = filter_ops.pop('attachment') - ports = [p for p in ports if p[1]['attachment'] == a] - - if filter_ops: - raise Exception("Need to implement files %s in " - "quantum fake client" % filter_ops) - - return {"ports": [{'id': p[0]} for p in ports]} - - def show_port_details(self, network, port, tenant=None): - """Fetches the details of a certain port""" - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - p = nets[network]['ports'][port] - return {"port": {"state": p["state"], "id": port}} - - def create_port(self, network, body=None, tenant=None): - """Creates a new port on a given network""" - nets = self._get_tenant_nets(tenant) - self._verify_net(network, nets) - uuid = str(utils.gen_uuid()) - nets[network]['ports'][uuid] = {'attachment': None, - 'state': body['port']['state']} - return {"port": {"id": uuid}} - - def delete_port(self, network, port, tenant=None): - """Deletes the specified port from a network""" - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - del nets[network]['ports'][port] - - def set_port_state(self, network, port, body=None, tenant=None): - """Sets the state of the specified port""" - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - - new_state = body['port']['state'] - nets[network]['ports'][port]['state'] = new_state - return {"port": {"state": new_state}} - - def show_port_attachment(self, network, port, tenant=None): - """Fetches the attachment-id associated with the specified port""" - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - p = nets[network]['ports'][port] - if p['attachment'] is not None: - return {"attachment": {"id": p['attachment']}} - else: - return {"attachment": {}} - - def attach_resource(self, network, port, body=None, tenant=None): - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - nets[network]['ports'][port]['attachment'] = body['attachment']['id'] - - def detach_resource(self, network, port, tenant=None): - """Removes the attachment-id of the specified port""" - nets = self._get_tenant_nets(tenant) - self._verify_net_and_port(network, port, nets) - nets[network]['ports'][port]['attachment'] = None diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py deleted file mode 100644 index 0ee902f61..000000000 --- a/nova/network/quantum/manager.py +++ /dev/null @@ -1,762 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Nicira Networks, Inc -# 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 time - -import netaddr - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova.network import manager -from nova.network.quantum import melange_ipam_lib -from nova.network.quantum import quantum_connection -from nova.openstack.common import cfg -from nova.openstack.common import log as logging -from nova.openstack.common import rpc -from nova import utils - -LOG = logging.getLogger(__name__) - -quantum_opts = [ - cfg.StrOpt('quantum_ipam_lib', - default='nova.network.quantum.nova_ipam_lib', - help="Indicates underlying IP address management library"), - # TODO(Vek): Eventually, this needs to mean more than just using - # Melange for assignment of MAC addresses (with an - # appropriate flag name change, of course), but this is all - # it does right now - cfg.BoolOpt('use_melange_mac_generation', - default=False, - help="Use Melange for assignment of MAC addresses"), - cfg.BoolOpt('quantum_use_dhcp', - default=False, - help='Whether or not to enable DHCP for networks'), - cfg.BoolOpt('quantum_use_port_security', - default=False, - help='Whether or not to enable port security'), - cfg.BoolOpt('quantum_port_security_include_link_local', - default=False, - help='Add the link local address to the port security list'), - ] - -FLAGS = flags.FLAGS -FLAGS.register_opts(quantum_opts) - - -class QuantumManager(manager.FloatingIP, manager.FlatManager): - """NetworkManager class that communicates with a Quantum service - via a web services API to provision VM network connectivity. - - For IP Address management, QuantumManager can be configured to - use either Nova's local DB or the Melange IPAM service. - """ - - DHCP = FLAGS.quantum_use_dhcp - - def __init__(self, q_conn=None, ipam_lib=None, *args, **kwargs): - """Initialize two key libraries, the connection to a - Quantum service, and the library for implementing IPAM. - - Calls inherited FlatManager constructor. - """ - - if not q_conn: - q_conn = quantum_connection.QuantumClientConnection() - self.q_conn = q_conn - - if not ipam_lib: - ipam_lib = FLAGS.quantum_ipam_lib - self._import_ipam_lib(ipam_lib) - - super(QuantumManager, self).__init__(*args, **kwargs) - - def init_host(self): - # Initialize general L3 networking - self.l3driver.initialize() - super(QuantumManager, self).init_host() - # Initialize floating ip support (only works for nova ipam currently) - if FLAGS.quantum_ipam_lib == 'nova.network.quantum.nova_ipam_lib': - LOG.debug("Initializing FloatingIP support") - self.init_host_floating_ips() - # Set up all the forwarding rules for any network that has a - # gateway set. - networks = self.get_all_networks(context.get_admin_context()) - cidrs = [] - for net in networks: - # Don't update host information for network that does not - # belong to you - if net['host'] != self.host: - continue - if net['gateway']: - LOG.debug("Initializing NAT: %s (cidr: %s, gw: %s)" % ( - net['label'], net['cidr'], net['gateway'])) - cidrs.append(net['cidr']) - self._update_network_host(context.get_admin_context(), - net['uuid']) - # .. and for each network - for c in cidrs: - self.l3driver.initialize_network(c) - - # Similar to FlatDHCPMananger, except we check for quantum_use_dhcp flag - # before we try to update_dhcp - def _setup_network_on_host(self, context, network): - """Sets up network on this host.""" - network['dhcp_server'] = self._get_dhcp_ip(context, network) - self.l3driver.initialize_gateway(network) - - if FLAGS.quantum_use_dhcp and not FLAGS.fake_network: - dev = self.driver.get_dev(network) - self.driver.update_dhcp(context, dev, network) - if FLAGS.use_ipv6: - self.driver.update_ra(context, dev, network) - gateway = utils.get_my_linklocal(dev) - self.db.network_update(context, network['id'], - {'gateway_v6': gateway}) - - def _update_network_host(self, context, net_uuid): - """Set the host column in the networks table: note that this won't - work with multi-host but QuantumManager doesn't support that - anyways. The floating IPs mixin required network['host'] to be - set.""" - entry = db.network_get_by_uuid(context.elevated(), net_uuid) - entry['host'] = self.host - db.network_update(context.elevated(), entry['id'], entry) - - def _get_nova_id(self, instance=None): - # When creating the network we need to pass in an identifier for - # this zone. Some Quantum plugins need this information in order - # to set up appropriate networking. - if instance and instance['availability_zone']: - return instance['availability_zone'] - else: - return FLAGS.node_availability_zone - - def get_all_networks(self, context): - networks = [] - networks.extend(self.ipam.get_global_networks(context)) - networks.extend(self.ipam.get_project_networks(context)) - return networks - - def create_networks(self, context, label, cidr, multi_host, num_networks, - network_size, cidr_v6, gateway, gateway_v6, bridge, - bridge_interface, dns1=None, dns2=None, uuid=None, - **kwargs): - """Unlike other NetworkManagers, with QuantumManager, each - create_networks calls should create only a single network. - - Two scenarios exist: - - no 'uuid' is specified, in which case we contact - Quantum and create a new network. - - an existing 'uuid' is specified, corresponding to - a Quantum network created out of band. - - In both cases, we initialize a subnet using the IPAM lib. - """ - # Enforce Configuration sanity. - # - # These flags are passed in from bin/nova-manage. The script - # collects the arguments and then passes them in through this - # function call. Note that in some cases, the script pre-processes - # the arguments, and sets them to a default value if a parameter's - # value was not specified on the command line. For pre-processed - # parameters, the most effective check to see if the user passed it - # in is to see if is different from the default value. (This - # does miss the use case where the user passes in the default value - # on the command line -- but it is unavoidable.) - if multi_host != FLAGS.multi_host: - # User specified it on the command line. - raise Exception(_("QuantumManager does not use 'multi_host'" - " parameter.")) - - if num_networks != 1: - raise Exception(_("QuantumManager requires that only one" - " network is created per call")) - - if network_size != int(FLAGS.network_size): - # User specified it on the command line. - LOG.warning("Ignoring unnecessary parameter 'network_size'") - - if kwargs.get('vlan_start', None): - if kwargs['vlan_start'] != int(FLAGS.vlan_start): - # User specified it on the command line. - LOG.warning(_("QuantumManager does not use 'vlan_start'" - " parameter.")) - - if kwargs.get('vpn_start', None): - if kwargs['vpn_start'] != int(FLAGS.vpn_start): - # User specified it on the command line. - LOG.warning(_("QuantumManager does not use 'vpn_start'" - " parameter.")) - - if bridge is not None and len(bridge) > 0: - LOG.warning(_("QuantumManager does not use 'bridge'" - " parameter.")) - - if bridge_interface is not None and len(bridge_interface) > 0: - LOG.warning(_("QuantumManager does not use 'bridge_interface'" - " parameter.")) - - if gateway is not None and len(gateway) > 0: - if gateway.split('.')[3] != '1': - raise Exception(_("QuantumManager requires a valid (.1)" - " gateway address.")) - - q_tenant_id = kwargs["project_id"] or FLAGS.quantum_default_tenant_id - quantum_net_id = uuid - # If a uuid was specified with the network it should have already been - # created in Quantum, so make sure. - if quantum_net_id: - if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): - raise Exception(_("Unable to find existing quantum " - "network for tenant '%(q_tenant_id)s' " - "with net-id '%(quantum_net_id)s'") % - locals()) - else: - nova_id = self._get_nova_id() - quantum_net_id = self.q_conn.create_network(q_tenant_id, label, - nova_id=nova_id) - - ipam_tenant_id = kwargs.get("project_id", None) - priority = kwargs.get("priority", 0) - # NOTE(tr3buchet): this call creates a nova network in the nova db - self.ipam.create_subnet(context, label, ipam_tenant_id, - quantum_net_id, priority, cidr, - gateway, gateway_v6, cidr_v6, dns1, dns2) - - self._update_network_host(context, quantum_net_id) - - # Initialize forwarding - self.l3driver.initialize_network(cidr) - - return [{'uuid': quantum_net_id}] - - def delete_network(self, context, fixed_range, uuid): - """Lookup network by uuid, delete both the IPAM - subnet and the corresponding Quantum network. - - The fixed_range parameter is kept here for interface compatibility - but is not used. - """ - net_ref = db.network_get_by_uuid(context.elevated(), uuid) - project_id = net_ref['project_id'] - q_tenant_id = project_id or FLAGS.quantum_default_tenant_id - net_uuid = net_ref['uuid'] - - # Check for any attached ports on the network and fail the deletion if - # there is anything but the gateway port attached. If it is only the - # gateway port, unattach and delete it. - ports = self.q_conn.get_attached_ports(q_tenant_id, net_uuid) - num_ports = len(ports) - gw_interface_id = self.driver.get_dev(net_ref) - gw_port_uuid = None - if gw_interface_id is not None: - gw_port_uuid = self.q_conn.get_port_by_attachment(q_tenant_id, - net_uuid, gw_interface_id) - - if gw_port_uuid: - num_ports -= 1 - - if num_ports > 0: - raise exception.NetworkBusy(network=net_uuid) - - # only delete gw ports if we are going to finish deleting network - if gw_port_uuid: - self.q_conn.detach_and_delete_port(q_tenant_id, - net_uuid, - gw_port_uuid) - self.l3driver.remove_gateway(net_ref) - - # Now we can delete the network - self.q_conn.delete_network(q_tenant_id, net_uuid) - LOG.debug("Deleting network %s for tenant: %s" % - (net_uuid, q_tenant_id)) - self.ipam.delete_subnets_by_net_id(context, net_uuid, project_id) - # Get rid of dnsmasq - if FLAGS.quantum_use_dhcp: - if net_ref['host'] == self.host: - self.kill_dhcp(net_ref) - else: - topic = rpc.queue_get_for(context, - FLAGS.network_topic, - net_ref['host']) - - rpc.call(context, topic, {'method': 'kill_dhcp', - 'args': {'net_ref': net_ref}}) - - def kill_dhcp(self, net_ref): - dev = self.driver.get_dev(net_ref) - if self.driver._device_exists(dev): - self.driver.kill_dhcp(dev) - - def allocate_for_instance(self, context, **kwargs): - """Called by compute when it is creating a new VM. - - There are three key tasks: - - Determine the number and order of vNICs to create - - Allocate IP addresses - - Create ports on a Quantum network and attach vNICs. - - We support two approaches to determining vNICs: - - By default, a VM gets a vNIC for any network belonging - to the VM's project, and a vNIC for any "global" network - that has a NULL project_id. vNIC order is determined - by the network's 'priority' field. - - If the 'os-create-server-ext' was used to create the VM, - only the networks in 'requested_networks' are used to - create vNICs, and the vNIC order is determiend by the - order in the requested_networks array. - - For each vNIC, use the FlatManager to create the entries - in the virtual_interfaces table, contact Quantum to - create a port and attachment the vNIC, and use the IPAM - lib to allocate IP addresses. - """ - instance_id = kwargs['instance_id'] - rxtx_factor = kwargs['rxtx_factor'] - host = kwargs['host'] - project_id = kwargs['project_id'] - LOG.debug(_("network allocations for instance %s"), project_id) - requested_networks = kwargs.get('requested_networks') - instance = db.instance_get(context, instance_id) - - net_proj_pairs = self.ipam.get_project_and_global_net_ids(context, - project_id) - if requested_networks: - # need to figure out if a requested network is owned - # by the tenant, or by the provider - # Note: these are the only possible options, as the compute - # API already validated networks using validate_network() - proj_net_ids = set([p[0] for p in net_proj_pairs if p[1]]) - net_proj_pairs = [] - for net_id, _i in requested_networks: - if net_id in proj_net_ids: - net_proj_pairs.append((net_id, project_id)) - else: - net_proj_pairs.append((net_id, None)) - - # Create a port via quantum and attach the vif - for proj_pair in net_proj_pairs: - network = self.get_network(context, proj_pair) - - # TODO(tr3buchet): broken. Virtual interfaces require an integer - # network ID and it is not nullable - vif_rec = self.add_virtual_interface(context, - instance['uuid'], - network['id'], - project_id) - - # talk to Quantum API to create and attach port. - nova_id = self._get_nova_id(instance) - # Tell the ipam library to allocate an IP - ips = self.ipam.allocate_fixed_ips(context, project_id, - network['quantum_net_id'], network['net_tenant_id'], - vif_rec) - pairs = [] - # Set up port security if enabled - if FLAGS.quantum_use_port_security: - if FLAGS.quantum_port_security_include_link_local: - mac = netaddr.EUI(vif_rec['address']) - ips.append(str(mac.ipv6_link_local())) - - pairs = [{'mac_address': vif_rec['address'], - 'ip_address': ip} for ip in ips] - - self.q_conn.create_and_attach_port(network['net_tenant_id'], - network['quantum_net_id'], - vif_rec['uuid'], - vm_id=instance['uuid'], - rxtx_factor=rxtx_factor, - nova_id=nova_id, - allowed_address_pairs=pairs) - # Set up/start the dhcp server for this network if necessary - if FLAGS.quantum_use_dhcp: - if network['host'] == self.host: - self.enable_dhcp(context, network['quantum_net_id'], - network, vif_rec, network['net_tenant_id']) - else: - topic = rpc.queue_get_for(context, - FLAGS.network_topic, network['host']) - rpc.call(context, topic, {'method': 'enable_dhcp', - 'args': {'quantum_net_id': network['quantum_net_id'], - 'network_ref': network, - 'vif_rec': vif_rec, - 'project_id': network['net_tenant_id']}}) - - return self.get_instance_nw_info(context, instance_id, - instance['uuid'], - rxtx_factor, host, - project_id=project_id) - - def get_network(self, context, proj_pair): - (quantum_net_id, net_tenant_id) = proj_pair - - net_tenant_id = net_tenant_id or FLAGS.quantum_default_tenant_id - # FIXME(danwent): We'd like to have the manager be - # completely decoupled from the nova networks table. - # However, other parts of nova sometimes go behind our - # back and access network data directly from the DB. So - # for now, the quantum manager knows that there is a nova - # networks DB table and accesses it here. updating the - # virtual_interfaces table to use UUIDs would be one - # solution, but this would require significant work - # elsewhere. - admin_context = context.elevated() - - # We may not be able to get a network_ref here if this network - # isn't in the database (i.e. it came from Quantum). - network_ref = db.network_get_by_uuid(admin_context, - quantum_net_id) - - if network_ref is None: - network_ref = {} - network_ref = {"uuid": quantum_net_id, - "project_id": net_tenant_id, - # NOTE(bgh): We need to document this somewhere but since - # we don't know the priority of any networks we get from - # quantum we just give them a priority of 0. If its - # necessary to specify the order of the vifs and what - # network they map to then the user will have to use the - # OSCreateServer extension and specify them explicitly. - # - # In the future users will be able to tag quantum networks - # with a priority .. and at that point we can update the - # code here to reflect that. - "priority": 0, - "id": 'NULL', - "label": "quantum-net-%s" % quantum_net_id} - network_ref['net_tenant_id'] = net_tenant_id - network_ref['quantum_net_id'] = quantum_net_id - return network_ref - - @manager.wrap_check_policy - def get_instance_uuids_by_ip_filter(self, context, filters): - # This is not returning the instance IDs like the method name - # would make you think; it is matching the return format of - # the method it's overriding. - instance_ids = self.ipam.get_instance_ids_by_ip_address( - context, filters.get('ip')) - instances = [db.instance_get(context, id) for id in instance_ids] - return [{'instance_uuid':instance.uuid} for instance in instances] - - @utils.synchronized('quantum-enable-dhcp') - def enable_dhcp(self, context, quantum_net_id, network_ref, vif_rec, - project_id): - LOG.info("Using DHCP for network: %s" % network_ref['label']) - # Figure out the ipam tenant id for this subnet: We need to - # query for the tenant_id since the network could be created - # with the project_id as the tenant or the default tenant. - ipam_tenant_id = self.ipam.get_tenant_id_by_net_id(context, - quantum_net_id, vif_rec['uuid'], project_id) - # Figure out what subnets correspond to this network - subnets = self.ipam.get_subnets_by_net_id(context, - ipam_tenant_id, quantum_net_id, vif_rec['uuid']) - - # Set up (or find) the dhcp server for each of the subnets - # returned above (both v4 and v6). - for subnet in subnets: - if subnet is None or subnet['cidr'] is None: - continue - # Fill in some of the network fields that we would have - # previously gotten from the network table (they'll be - # passed to the linux_net functions). - network_ref['cidr'] = subnet['cidr'] - n = netaddr.IPNetwork(subnet['cidr']) - # NOTE(tr3buchet): should probably not always assume first+1 - network_ref['dhcp_server'] = netaddr.IPAddress(n.first + 1) - # TODO(bgh): Melange should probably track dhcp_start - # TODO(tr3buchet): melange should store dhcp_server as well - if (not 'dhcp_start' in network_ref or - network_ref['dhcp_start'] is None): - network_ref['dhcp_start'] = netaddr.IPAddress(n.first + 2) - network_ref['broadcast'] = netaddr.IPAddress(n.broadcast) - network_ref['gateway'] = subnet['gateway'] - # Construct the interface id that we'll use for the bridge - interface_id = self.driver.get_dev(network_ref) - network_ref['bridge'] = interface_id - # Query quantum to see if we've already created a port for - # the gateway device and attached the device to the port. - # If we haven't then we need to intiialize it and create - # it. This device will be the one serving dhcp via - # dnsmasq. - q_tenant_id = project_id or FLAGS.quantum_default_tenant_id - port = self.q_conn.get_port_by_attachment(q_tenant_id, - quantum_net_id, interface_id) - - if not port: # No dhcp server has been started - self.l3driver.initialize_gateway(network_ref) - LOG.debug("Intializing DHCP for network: %s" % - network_ref) - self.q_conn.create_and_attach_port(q_tenant_id, - quantum_net_id, interface_id) - - hosts = self.get_dhcp_hosts_text(context, - subnet['network_id'], project_id) - self.driver.update_dhcp_hostfile_with_text(interface_id, hosts) - self.driver.restart_dhcp(context, interface_id, network_ref) - - def add_virtual_interface(self, context, instance_uuid, network_id, - net_tenant_id): - # If we're not using melange, use the default means... - if FLAGS.use_melange_mac_generation: - return self._add_virtual_interface(context, instance_uuid, - network_id, net_tenant_id) - - return super(QuantumManager, self).add_virtual_interface(context, - instance_uuid, - network_id) - - def _add_virtual_interface(self, context, instance_uuid, network_id, - net_tenant_id): - vif = {'instance_uuid': instance_uuid, - 'network_id': network_id, - 'uuid': str(utils.gen_uuid())} - - # TODO(Vek): Ideally, we would have a VirtualInterface class - # that would take care of delegating to whoever it - # needs to get information from. We'll look at - # this after Trey's refactorings... - m_ipam = melange_ipam_lib.get_ipam_lib(self) - vif['address'] = m_ipam.create_vif(vif['uuid'], - vif['instance_uuid'], - net_tenant_id) - - return self.db.virtual_interface_create(context, vif) - - def get_instance_nw_info(self, context, instance_id, instance_uuid, - rxtx_factor, host, **kwargs): - """This method is used by compute to fetch all network data - that should be used when creating the VM. - - The method simply loops through all virtual interfaces - stored in the nova DB and queries the IPAM lib to get - the associated IP data. - - The format of returned data is 'defined' by the initial - set of NetworkManagers found in nova/network/manager.py . - Ideally this 'interface' will be more formally defined - in the future. - """ - project_id = kwargs['project_id'] - vifs = db.virtual_interface_get_by_instance(context, instance_uuid) - - net_tenant_dict = dict((net_id, tenant_id) - for (net_id, tenant_id) - in self.ipam.get_project_and_global_net_ids( - context, project_id)) - networks = {} - for vif in vifs: - if vif.get('network_id') is not None: - network = db.network_get(context.elevated(), vif['network_id']) - net_tenant_id = net_tenant_dict[network['uuid']] - if net_tenant_id is None: - net_tenant_id = FLAGS.quantum_default_tenant_id - network = {'id': network['id'], - 'uuid': network['uuid'], - 'bridge': '', # Quantum ignores this field - 'label': network['label'], - 'injected': FLAGS.flat_injected, - 'project_id': net_tenant_id} - networks[vif['uuid']] = network - - # update instance network cache and return network_info - nw_info = self.build_network_info_model(context, vifs, networks, - rxtx_factor, host) - db.instance_info_cache_update(context, instance_uuid, - {'network_info': nw_info.json()}) - - return nw_info - - def deallocate_for_instance(self, context, **kwargs): - """Called when a VM is terminated. Loop through each virtual - interface in the Nova DB and remove the Quantum port and - clear the IP allocation using the IPAM. Finally, remove - the virtual interfaces from the Nova DB. - """ - instance_id = kwargs.get('instance_id') - project_id = kwargs.pop('project_id', None) - - admin_context = context.elevated() - instance = db.instance_get(context, instance_id) - vifs = db.virtual_interface_get_by_instance(admin_context, - instance['uuid']) - - for vif in vifs: - network = db.network_get(admin_context, vif['network_id']) - - self.deallocate_port(vif['uuid'], network['uuid'], project_id, - instance_id) - - ipam_tenant_id = self.deallocate_ip_address(context, - network['uuid'], project_id, vif, instance_id) - - if FLAGS.quantum_use_dhcp: - if network['host'] == self.host: - self.update_dhcp(context, ipam_tenant_id, network, - vif, project_id) - else: - topic = rpc.queue_get_for(context, - FLAGS.network_topic, network['host']) - rpc.call(context, topic, {'method': 'update_dhcp', - 'args': {'ipam_tenant_id': ipam_tenant_id, - 'network_ref': network, - 'vif_ref': vif, - 'project_id': network['project_id']}}) - - db.virtual_interface_delete(admin_context, vif['id']) - - def deallocate_port(self, interface_id, net_id, q_tenant_id, instance_id): - port_id = None - try: - port_id = self.q_conn.get_port_by_attachment(q_tenant_id, - net_id, interface_id) - if not port_id: - q_tenant_id = FLAGS.quantum_default_tenant_id - port_id = self.q_conn.get_port_by_attachment( - q_tenant_id, net_id, interface_id) - - if not port_id: - LOG.error("Unable to find port with attachment: %s" % - (interface_id)) - else: - self.q_conn.detach_and_delete_port(q_tenant_id, - net_id, port_id) - except Exception: - # except anything so the rest of deallocate can succeed - LOG.exception(_('port deallocation failed for instance: ' - '|%(instance_id)s|, port_id: |%(port_id)s|') % locals()) - - def deallocate_ip_address(self, context, net_id, - project_id, vif_ref, instance_id): - ipam_tenant_id = None - try: - ipam_tenant_id = self.ipam.get_tenant_id_by_net_id(context, - net_id, - vif_ref['uuid'], - project_id) - - self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id, - net_id, vif_ref) - - except Exception: - # except anything so the rest of deallocate can succeed - vif_uuid = vif_ref['uuid'] - msg = _('ipam deallocation failed for instance: ' - '|%(instance_id)s|, vif_uuid: |%(vif_uuid)s|') % locals() - LOG.exception(msg) - return ipam_tenant_id - - # enable_dhcp() could also use this - # - # Use semaphore to : - # 1) avoid race between restart_dhcps - # 2) avoid race between update_dhcp_hostfile_with_texts - @utils.synchronized('quantum-restart-dhcp') - def _atomic_restart_dhcp(self, context, dev, hosts, network_ref): - self.driver.update_dhcp_hostfile_with_text(dev, hosts) - # Since we only update hosts file, explict kill_dhcp() isn't needed. - # restart_dhcp() will reload hostfile if dnsmasq is already running, - # or start new dnsmasq - self.driver.restart_dhcp(context, dev, network_ref) - - # TODO(bgh): At some point we should consider merging enable_dhcp() and - # update_dhcp() - # TODO(tr3buchet): agree, i'm curious why they differ even now.. - def update_dhcp(self, context, ipam_tenant_id, network_ref, vif_ref, - project_id): - # Figure out what subnet corresponds to this network/vif - subnets = self.ipam.get_subnets_by_net_id(context, - ipam_tenant_id, network_ref['uuid'], vif_ref['uuid']) - for subnet in subnets: - if subnet is None: - continue - # Fill in some of the network fields that we would have - # previously gotten from the network table (they'll be - # passed to the linux_net functions). - if subnet['cidr']: - network_ref['cidr'] = subnet['cidr'] - n = netaddr.IPNetwork(network_ref['cidr']) - network_ref['dhcp_server'] = netaddr.IPAddress(n.first + 1) - network_ref['dhcp_start'] = netaddr.IPAddress(n.first + 2) - network_ref['broadcast'] = netaddr.IPAddress(n.broadcast) - network_ref['gateway'] = netaddr.IPAddress(n.first + 1) - dev = self.driver.get_dev(network_ref) - # And remove the dhcp mappings for the subnet - hosts = self.get_dhcp_hosts_text(context, - subnet['network_id'], project_id) - - self._atomic_restart_dhcp(context, dev, hosts, network_ref) - - def validate_networks(self, context, networks): - """Validates that this tenant has quantum networks with the associated - UUIDs. This is called by the 'os-create-server-ext' API extension - code so that we can return an API error code to the caller if they - request an invalid network. - """ - if networks is None: - return - - project_id = context.project_id - for (net_id, _i) in networks: - # TODO(bgh): At some point we should figure out whether or - # not we want the verify_subnet_exists call to be optional. - if not self.ipam.verify_subnet_exists(context, project_id, - net_id): - raise exception.NetworkNotFound(network_id=net_id) - is_tenant_net = self.q_conn.network_exists(project_id, net_id) - is_provider_net = self.q_conn.network_exists( - FLAGS.quantum_default_tenant_id, - net_id) - if not (is_tenant_net or is_provider_net): - raise exception.NetworkNotFound(network_id=net_id) - - def get_dhcp_hosts_text(self, context, subnet_id, project_id=None): - ips = self.ipam.get_allocated_ips(context, subnet_id, project_id) - hosts_text = "" - admin_context = context.elevated() - for ip in ips: - address, vif_id = ip - vif = db.virtual_interface_get_by_uuid(admin_context, vif_id) - mac_address = vif['address'] - text = "%s,%s.%s,%s\n" % (mac_address, "host-" + address, - FLAGS.dhcp_domain, address) - hosts_text += text - LOG.debug("DHCP hosts: %s" % hosts_text) - return hosts_text - - def get_dhcp_leases(self, context, network_ref): - """Return a network's hosts config in dnsmasq leasefile format.""" - subnet_id = network_ref['uuid'] - project_id = network_ref['project_id'] - ips = self.ipam.get_allocated_ips(context, subnet_id, project_id) - leases_text = "" - admin_context = context.elevated() - for ip in ips: - address, vif_id = ip - vif = db.virtual_interface_get_by_uuid(admin_context, vif_id) - mac_address = vif['address'] - text = ("%s %s %s %s *\n" % (int(time.time()) - - FLAGS.dhcp_lease_time, - mac_address, address, '*')) - leases_text += text - LOG.debug("DHCP leases: %s" % leases_text) - return leases_text - - def setup_networks_on_host(self, *args, **kwargs): - # no host specific setup is needed in quantum manager - pass diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py deleted file mode 100644 index 342222909..000000000 --- a/nova/network/quantum/melange_connection.py +++ /dev/null @@ -1,202 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 httplib -import socket -import time -import urllib - -from nova import exception -from nova import flags -from nova.openstack.common import cfg -from nova.openstack.common import jsonutils -from nova.openstack.common import log as logging - - -melange_opts = [ - cfg.StrOpt('melange_host', - default='127.0.0.1', - help='HOST for connecting to melange'), - cfg.IntOpt('melange_port', - default=9898, - help='PORT for connecting to melange'), - cfg.IntOpt('melange_num_retries', - default=0, - help='Number retries when contacting melange'), - ] - -FLAGS = flags.FLAGS -FLAGS.register_opts(melange_opts) -LOG = logging.getLogger(__name__) - -json_content_type = {'Content-type': "application/json"} - - -# FIXME(danwent): talk to the Melange folks about creating a -# client lib that we can import as a library, instead of -# have to have all of the client code in here. -class MelangeConnection(object): - - def __init__(self, host=None, port=None, use_ssl=False): - if host is None: - host = FLAGS.melange_host - if port is None: - port = FLAGS.melange_port - self.host = host - self.port = port - self.use_ssl = use_ssl - self.version = "v0.1" - - def get(self, path, params=None, headers=None): - return self.do_request("GET", path, params=params, headers=headers, - retries=FLAGS.melange_num_retries) - - def post(self, path, body=None, headers=None): - return self.do_request("POST", path, body=body, headers=headers) - - def delete(self, path, headers=None): - return self.do_request("DELETE", path, headers=headers) - - def _get_connection(self): - if self.use_ssl: - return httplib.HTTPSConnection(self.host, self.port) - else: - return httplib.HTTPConnection(self.host, self.port) - - def do_request(self, method, path, body=None, headers=None, params=None, - content_type=".json", retries=0): - headers = headers or {} - params = params or {} - - url = "/%s/%s%s" % (self.version, path, content_type) - if params: - url += "?%s" % urllib.urlencode(params) - for i in xrange(retries + 1): - connection = self._get_connection() - try: - connection.request(method, url, body, headers) - response = connection.getresponse() - response_str = response.read() - if response.status < 400: - return response_str - raise Exception(_("Server returned error: %s") % response_str) - except (socket.error, IOError), e: - LOG.exception(_('Connection error contacting melange' - ' service, retrying')) - - time.sleep(1) - - raise exception.MelangeConnectionFailed( - reason=_("Maximum attempts reached")) - - def allocate_ip(self, network_id, network_tenant_id, vif_id, - project_id=None, mac_address=None): - LOG.info(_("allocate IP on network |%(network_id)s| " - "belonging to |%(network_tenant_id)s| " - "to this vif |%(vif_id)s| with mac |%(mac_address)s| " - "belonging to |%(project_id)s| ") % locals()) - tenant_scope = "/tenants/%s" % (network_tenant_id - if network_tenant_id else "") - network_info = dict(network=dict(mac_address=mac_address, - tenant_id=project_id)) - request_body = jsonutils.dumps(network_info) if mac_address else None - url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" - "interfaces/%(vif_id)s/ip_allocations" % locals()) - response = self.post(url, body=request_body, headers=json_content_type) - return jsonutils.loads(response)['ip_addresses'] - - def create_block(self, network_id, cidr, - project_id=None, gateway=None, dns1=None, dns2=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = "ipam%(tenant_scope)s/ip_blocks" % locals() - - req_params = dict(ip_block=dict(cidr=cidr, network_id=network_id, - type='private', gateway=gateway, - dns1=dns1, dns2=dns2)) - self.post(url, - body=jsonutils.dumps(req_params), - headers=json_content_type) - - def delete_block(self, block_id, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = "ipam%(tenant_scope)s/ip_blocks/%(block_id)s" % locals() - - self.delete(url, headers=json_content_type) - - def get_blocks(self, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = "ipam%(tenant_scope)s/ip_blocks" % locals() - - response = self.get(url, headers=json_content_type) - return jsonutils.loads(response) - - def get_routes(self, block_id, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = ("ipam%(tenant_scope)s/ip_blocks/%(block_id)s/ip_routes" % - locals()) - - response = self.get(url, headers=json_content_type) - return jsonutils.loads(response)['ip_routes'] - - def get_allocated_ips(self, network_id, vif_id, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" - "interfaces/%(vif_id)s/ip_allocations" % locals()) - - response = self.get(url, headers=json_content_type) - return jsonutils.loads(response)['ip_addresses'] - - def get_allocated_ips_by_address(self, address): - url = "ipam/allocated_ip_addresses" - - response = self.get(url, params={'address': address}, - headers=json_content_type) - - return jsonutils.loads(response).get('ip_addresses', []) - - def get_allocated_ips_for_network(self, network_id, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = ("ipam%(tenant_scope)s/allocated_ip_addresses" % locals()) - # TODO(bgh): This request fails if you add the ".json" to the end so - # it has to call do_request itself. Melange bug? - response = self.do_request("GET", url, content_type="") - return jsonutils.loads(response)['ip_addresses'] - - def deallocate_ips(self, network_id, vif_id, project_id=None): - tenant_scope = "/tenants/%s" % project_id if project_id else "" - - url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" - "interfaces/%(vif_id)s/ip_allocations" % locals()) - - self.delete(url, headers=json_content_type) - - def create_vif(self, vif_id, instance_id, project_id=None): - url = "ipam/interfaces" - - request_body = dict(interface=dict(id=vif_id, tenant_id=project_id, - device_id=instance_id)) - - response = self.post(url, - body=jsonutils.dumps(request_body), - headers=json_content_type) - - return jsonutils.loads(response)['interface']['mac_address'] diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py deleted file mode 100644 index 44f4b3c69..000000000 --- a/nova/network/quantum/melange_ipam_lib.py +++ /dev/null @@ -1,261 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Nicira Networks, Inc -# 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 - -from nova import db -from nova import exception -from nova import flags -from nova.network.quantum import melange_connection -from nova.openstack.common import log as logging - - -LOG = logging.getLogger(__name__) - -FLAGS = flags.FLAGS - - -def get_ipam_lib(net_man): - return QuantumMelangeIPAMLib() - - -class QuantumMelangeIPAMLib(object): - """Implements Quantum IP Address Management (IPAM) interface - using the Melange service, which is access using the Melange - web services API. - """ - - def __init__(self): - """Initialize class used to connect to Melange server""" - self.m_conn = melange_connection.MelangeConnection() - - def create_subnet(self, context, label, project_id, - quantum_net_id, priority, cidr=None, - gateway=None, gateway_v6=None, cidr_v6=None, - dns1=None, dns2=None): - """Contact Melange and create a subnet for any non-NULL - IPv4 or IPv6 subnets. - - Also create an entry in the Nova networks DB, but only - to store values not represented in Melange or to - temporarily provide compatibility with Nova code that - accesses IPAM data directly via the DB (e.g., nova-api) - """ - tenant_id = project_id or FLAGS.quantum_default_tenant_id - if cidr: - self.m_conn.create_block(quantum_net_id, cidr, - project_id=tenant_id, - gateway=gateway, - dns1=dns1, dns2=dns2) - if cidr_v6: - self.m_conn.create_block(quantum_net_id, cidr_v6, - project_id=tenant_id, - gateway=gateway_v6, - dns1=dns1, dns2=dns2) - - net = {"uuid": quantum_net_id, - "project_id": tenant_id, - "priority": priority, - "label": label} - if FLAGS.quantum_use_dhcp: - if cidr: - n = netaddr.IPNetwork(cidr) - net['dhcp_start'] = netaddr.IPAddress(n.first + 2) - else: - net['dhcp_start'] = None - admin_context = context.elevated() - network = db.network_create_safe(admin_context, net) - - def allocate_fixed_ips(self, context, project_id, quantum_net_id, - network_tenant_id, vif_ref): - """Pass call to allocate fixed IP on to Melange""" - ips = self.m_conn.allocate_ip(quantum_net_id, network_tenant_id, - vif_ref['uuid'], project_id, - vif_ref['address']) - return [ip['address'] for ip in ips] - - def delete_subnets_by_net_id(self, context, net_id, project_id): - """Find Melange block associated with the Quantum UUID, - then tell Melange to delete that block. - """ - admin_context = context.elevated() - tenant_id = project_id or FLAGS.quantum_default_tenant_id - all_blocks = self.m_conn.get_blocks(tenant_id) - for b in all_blocks['ip_blocks']: - if b['network_id'] == net_id: - self.m_conn.delete_block(b['id'], tenant_id) - - network = db.network_get_by_uuid(admin_context, net_id) - db.network_delete_safe(context, network['id']) - - def get_networks_by_tenant(self, admin_context, tenant_id): - nets = {} - blocks = self.m_conn.get_blocks(tenant_id) - for ip_block in blocks['ip_blocks']: - network_id = ip_block['network_id'] - network = db.network_get_by_uuid(admin_context, network_id) - nets[network_id] = network - return nets.values() - - def get_global_networks(self, admin_context): - return self.get_networks_by_tenant(admin_context, - FLAGS.quantum_default_tenant_id) - - def get_project_networks(self, admin_context): - try: - nets = db.network_get_all(admin_context.elevated()) - except exception.NoNetworksFound: - return [] - # only return networks with a project_id set - return [net for net in nets if net['project_id']] - - def get_project_and_global_net_ids(self, context, project_id): - """Fetches all networks associated with this project, or - that are "global" (i.e., have no project set). - Returns list sorted by 'priority' (lowest integer value - is highest priority). - """ - if project_id is None: - raise Exception(_("get_project_and_global_net_ids must be called" - " with a non-null project_id")) - - admin_context = context.elevated() - - # Decorate with priority - priority_nets = [] - for tenant_id in (project_id, FLAGS.quantum_default_tenant_id): - nets = self.get_networks_by_tenant(admin_context, tenant_id) - for network in nets: - priority = network['priority'] - priority_nets.append((priority, network['uuid'], tenant_id)) - - # Sort by priority - priority_nets.sort() - - # Undecorate - return [(network_id, tenant_id) - for priority, network_id, tenant_id in priority_nets] - - def get_tenant_id_by_net_id(self, context, net_id, vif_id, project_id): - ipam_tenant_id = None - tenant_ids = [FLAGS.quantum_default_tenant_id, project_id, None] - # This is confusing, if there are IPs for the given net, vif, - # tenant trifecta we assume that is the tenant for that network - for tid in tenant_ids: - try: - self.m_conn.get_allocated_ips(net_id, vif_id, tid) - except Exception: - continue - ipam_tenant_id = tid - break - return ipam_tenant_id - - # TODO(bgh): Rename this method .. it's now more of a - # "get_subnets_by_net_id_and_vif_id" method, but we could probably just - # call it "get_subnets". - def get_subnets_by_net_id(self, context, tenant_id, net_id, vif_id): - """Returns information about the IPv4 and IPv6 subnets - associated with a Quantum Network UUID. - """ - subnets = [] - ips = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) - - for ip_address in ips: - block = ip_address['ip_block'] - subnet = {'network_id': block['network_id'], - 'id': block['id'], - 'cidr': block['cidr'], - 'gateway': block['gateway'], - 'broadcast': block['broadcast'], - 'netmask': block['netmask'], - 'dns1': block['dns1'], - 'dns2': block['dns2']} - if ip_address['version'] == 4: - subnet['version'] = 4 - else: - subnet['version'] = 6 - subnets.append(subnet) - return subnets - - def get_routes_by_ip_block(self, context, block_id, project_id): - """Returns the list of routes for the IP block""" - return self.m_conn.get_routes(block_id, project_id) - - def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): - """Returns a list of IPv4 address strings associated with - the specified virtual interface. - """ - return self._get_ips_by_interface(context, net_id, vif_id, - project_id, 4) - - def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): - """Returns a list of IPv6 address strings associated with - the specified virtual interface. - """ - return self._get_ips_by_interface(context, net_id, vif_id, - project_id, 6) - - def _get_ips_by_interface(self, context, net_id, vif_id, project_id, - ip_version): - """Helper method to fetch v4 or v6 addresses for a particular - virtual interface. - """ - tenant_id = project_id or FLAGS.quantum_default_tenant_id - ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) - return [ip['address'] for ip in ip_list - if netaddr.IPNetwork(ip['address']).version == ip_version] - - def get_instance_ids_by_ip_address(self, context, address): - ips = self.m_conn.get_allocated_ips_by_address(address) - # TODO(aaron.lee): melange should be storing & returning instance_uuid! - return [ip.get('used_by_device') for ip in ips] - - def verify_subnet_exists(self, context, project_id, quantum_net_id): - """Confirms that a subnet exists that is associated with the - specified Quantum Network UUID. - """ - # TODO(bgh): Would be nice if we could just do something like: - # GET /ipam/tenants/{tenant_id}/networks/{network_id}/ instead - # of searching through all the blocks. Checking for a 404 - # will then determine whether it exists. - tenant_id = project_id or FLAGS.quantum_default_tenant_id - all_blocks = self.m_conn.get_blocks(tenant_id) - for b in all_blocks['ip_blocks']: - if b['network_id'] == quantum_net_id: - return True - return False - - def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref): - """Deallocate all fixed IPs associated with the specified - virtual interface. - """ - tenant_id = project_id or FLAGS.quantum_default_tenant_id - self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id) - - def get_allocated_ips(self, context, subnet_id, project_id): - ips = self.m_conn.get_allocated_ips_for_network(subnet_id, project_id) - return [(ip['address'], ip['interface_id']) for ip in ips] - - def create_vif(self, vif_id, instance_id, project_id=None): - """Create a new vif with the specified information. - """ - tenant_id = project_id or FLAGS.quantum_default_tenant_id - return self.m_conn.create_vif(vif_id, instance_id, tenant_id) - - def get_floating_ips_by_fixed_address(self, context, fixed_address): - """This call is not supported in quantum yet""" - return [] diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py deleted file mode 100644 index 2161c7916..000000000 --- a/nova/network/quantum/quantum_connection.py +++ /dev/null @@ -1,159 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Nicira Networks -# 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. - -from nova import flags -from nova.network.quantum import client as quantum_client -from nova.openstack.common import cfg -from nova.openstack.common import log as logging - - -LOG = logging.getLogger(__name__) - -quantum_opts = [ - cfg.StrOpt('quantum_connection_host', - default='127.0.0.1', - help='HOST for connecting to quantum'), - cfg.IntOpt('quantum_connection_port', - default=9696, - help='PORT for connecting to quantum'), - cfg.StrOpt('quantum_default_tenant_id', - default="default", - help='Default tenant id when creating quantum networks'), - cfg.IntOpt('quantum_request_timeout', - default=20, - help='Maximum amount of time to wait for quantum request'), - ] - -FLAGS = flags.FLAGS -FLAGS.register_opts(quantum_opts) - - -class QuantumClientConnection(object): - """Abstracts connection to Quantum service into higher level - operations performed by the QuantumManager. - - Separating this out as a class also let's us create a 'fake' - version of this class for unit tests. - """ - - def __init__(self, client=None): - """Initialize Quantum client class based on flags.""" - if client: - self.client = client - else: - self.client = quantum_client.Client(FLAGS.quantum_connection_host, - FLAGS.quantum_connection_port, - timeout=FLAGS.quantum_request_timeout, - format="json", - logger=LOG) - - def create_network(self, tenant_id, network_name, **kwargs): - """Create network using specified name, return Quantum - network UUID. - """ - data = {'network': {'name': network_name}} - for kw in kwargs: - data['network'][kw] = kwargs[kw] - resdict = self.client.create_network(data, tenant=tenant_id) - return resdict["network"]["id"] - - def get_network_name(self, tenant_id, network_id): - net = self.client.show_network_details(network_id, tenant=tenant_id) - return net["network"]["name"] - - def delete_network(self, tenant_id, net_id): - """Deletes Quantum network with specified UUID.""" - self.client.delete_network(net_id, tenant=tenant_id) - - def network_exists(self, tenant_id, net_id): - """Determine if a Quantum network exists for the - specified tenant. - """ - try: - self.client.show_network_details(net_id, tenant=tenant_id) - return True - except quantum_client.QuantumNotFoundException: - # Not really an error. Real errors will be propogated to caller - return False - - def get_networks(self, tenant_id): - """Retrieve all networks for this tenant""" - return self.client.list_networks(tenant=tenant_id) - - def create_and_attach_port(self, tenant_id, net_id, interface_id, - **kwargs): - """Creates a Quantum port on the specified network, sets - status to ACTIVE to enable traffic, and attaches the - vNIC with the specified interface-id. - """ - LOG.debug(_("Connecting interface %(interface_id)s to " - "net %(net_id)s for %(tenant_id)s"), locals()) - port_data = {'port': {'state': 'ACTIVE'}} - for kw in kwargs: - port_data['port'][kw] = kwargs[kw] - resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) - port_id = resdict["port"]["id"] - - attach_data = {'attachment': {'id': interface_id}} - self.client.attach_resource(net_id, port_id, attach_data, - tenant=tenant_id) - - def detach_and_delete_port(self, tenant_id, net_id, port_id): - """Detach and delete the specified Quantum port.""" - LOG.debug(_("Deleting port %(port_id)s on net %(net_id)s" - " for %(tenant_id)s"), locals()) - - self.client.detach_resource(net_id, port_id, tenant=tenant_id) - self.client.delete_port(net_id, port_id, tenant=tenant_id) - - def get_port_by_attachment(self, tenant_id, net_id, attachment_id): - """Given a tenant and network, search for the port UUID that - has the specified interface-id attachment. - """ - port_list = [] - try: - port_list_resdict = self.client.list_ports(net_id, - tenant=tenant_id, - filter_ops={'attachment': attachment_id}) - port_list = port_list_resdict["ports"] - except quantum_client.QuantumNotFoundException: - return None - - port_list_len = len(port_list) - if port_list_len == 1: - return port_list[0]['id'] - elif port_list_len > 1: - raise Exception("Expected single port with attachment " - "%(attachment_id)s, found %(port_list_len)s" % locals()) - return None - - def get_attached_ports(self, tenant_id, network_id): - rv = [] - port_list = self.client.list_ports(network_id, tenant=tenant_id) - for p in port_list["ports"]: - try: - port_id = p["id"] - port = self.client.show_port_attachment(network_id, - port_id, tenant=tenant_id) - # Skip ports without an attachment - if "id" not in port["attachment"]: - continue - rv.append({'port-id': port_id, - 'attachment': port["attachment"]["id"]}) - except quantum_client.QuantumNotFoundException: - pass - return rv diff --git a/nova/network/quantum/sg.py b/nova/network/sg.py index aad9f1052..aad9f1052 100644 --- a/nova/network/quantum/sg.py +++ b/nova/network/sg.py diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 5c945fa75..476d7eab4 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -20,7 +20,7 @@ from nova import db from nova import exception from nova import flags from nova.network import manager as network_manager -from nova.network.quantum import nova_ipam_lib +from nova.network import nova_ipam_lib from nova import utils diff --git a/nova/tests/network/test_quantum.py b/nova/tests/network/test_quantum.py deleted file mode 100644 index b126994d0..000000000 --- a/nova/tests/network/test_quantum.py +++ /dev/null @@ -1,647 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011,2012 Nicira, Inc. -# 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 mox - -from nova import context -from nova import db -from nova.db.sqlalchemy import models -from nova.db.sqlalchemy import session as sql_session -from nova import exception -from nova import flags -from nova.network.quantum import client as quantum_client -from nova.network.quantum import fake_client -from nova.network.quantum import manager as quantum_manager -from nova.network.quantum import melange_connection -from nova.network.quantum import melange_ipam_lib -from nova.network.quantum import quantum_connection -from nova.openstack.common import log as logging - -from nova import test -from nova import utils - -LOG = logging.getLogger(__name__) -FLAGS = flags.FLAGS - - -networks = [{'label': 'project1-net1', - 'injected': False, - 'multi_host': False, - 'cidr': '100.168.0.0/24', - 'cidr_v6': '100:1db8::/64', - 'gateway_v6': '100:1db8::1', - 'netmask_v6': '64', - 'netmask': '255.255.255.0', - 'bridge': None, - 'bridge_interface': None, - 'gateway': '100.168.0.1', - 'broadcast': '100.168.0.255', - 'dns1': '8.8.8.8', - 'vlan': None, - 'host': None, - 'vpn_public_address': None, - 'project_id': 'fake_project1', - 'priority': 1}, - {'label': 'project2-net1', - 'injected': False, - 'multi_host': False, - 'cidr': '101.168.1.0/24', - 'cidr_v6': '101:1db9::/64', - 'gateway_v6': '101:1db9::1', - 'netmask_v6': '64', - 'netmask': '255.255.255.0', - 'bridge': None, - 'bridge_interface': None, - 'gateway': '101.168.1.1', - 'broadcast': '101.168.1.255', - 'dns1': '8.8.8.8', - 'vlan': None, - 'host': None, - 'project_id': 'fake_project2', - 'priority': 1}, - {'label': "public", - 'injected': False, - 'multi_host': False, - 'cidr': '102.0.0.0/24', - 'cidr_v6': '102:1dba::/64', - 'gateway_v6': '102:1dba::1', - 'netmask_v6': '64', - 'netmask': '255.255.255.0', - 'bridge': None, - 'bridge_interface': None, - 'gateway': '102.0.0.1', - 'broadcast': '102.0.0.255', - 'dns1': '8.8.8.8', - 'vlan': None, - 'host': None, - 'project_id': None, - 'priority': 0}, - {'label': "project2-net2", - 'injected': False, - 'multi_host': False, - 'cidr': '103.0.0.0/24', - 'cidr_v6': '103:1dbb::/64', - 'gateway_v6': '103:1dbb::1', - 'netmask_v6': '64', - 'netmask': '255.255.255.0', - 'bridge': None, - 'bridge_interface': None, - 'gateway': '103.0.0.1', - 'broadcast': '103.0.0.255', - 'dns1': '8.8.8.8', - 'vlan': None, - 'host': None, - 'project_id': "fake_project2", - 'priority': 2}] - - -class QuantumConnectionTestCase(test.TestCase): - - def test_connection(self): - fc = fake_client.FakeClient(LOG) - qc = quantum_connection.QuantumClientConnection(client=fc) - t = "tenant1" - net1_name = "net1" - net1_uuid = qc.create_network(t, net1_name) - self.assertEquals(net1_name, qc.get_network_name(t, net1_uuid)) - self.assertTrue(qc.network_exists(t, net1_uuid)) - self.assertFalse(qc.network_exists(t, "fake-uuid")) - self.assertFalse(qc.network_exists("fake-tenant", net1_uuid)) - - nets = qc.get_networks(t)['networks'] - self.assertEquals(len(nets), 1) - self.assertEquals(nets[0]['id'], net1_uuid) - - num_ports = 10 - for i in range(0, num_ports): - qc.create_and_attach_port(t, net1_uuid, - 'iface' + str(i), state='ACTIVE') - - self.assertEquals(len(qc.get_attached_ports(t, net1_uuid)), num_ports) - - for i in range(0, num_ports): - port_uuid = qc.get_port_by_attachment(t, net1_uuid, - 'iface' + str(i)) - self.assertTrue(port_uuid) - qc.detach_and_delete_port(t, net1_uuid, port_uuid) - - self.assertEquals(len(qc.get_attached_ports(t, net1_uuid)), 0) - - # test port not found - qc.create_and_attach_port(t, net1_uuid, 'foo', state='ACTIVE') - port_uuid = qc.get_port_by_attachment(t, net1_uuid, 'foo') - qc.detach_and_delete_port(t, net1_uuid, port_uuid) - self.assertRaises(quantum_client.QuantumNotFoundException, - qc.detach_and_delete_port, t, - net1_uuid, port_uuid) - - qc.delete_network(t, net1_uuid) - self.assertFalse(qc.network_exists(t, net1_uuid)) - self.assertEquals(len(qc.get_networks(t)['networks']), 0) - - self.assertRaises(quantum_client.QuantumNotFoundException, - qc.get_network_name, t, net1_uuid) - - -# this is a base class to be used by other QuantumManager Test classes -class QuantumNovaTestCase(test.TestCase): - - def setUp(self): - super(QuantumNovaTestCase, self).setUp() - - self.flags(quantum_use_dhcp=True) - self.flags(l3_lib="nova.network.l3.LinuxNetL3") - linuxdrv = "nova.network.linux_net.LinuxOVSInterfaceDriver" - self.flags(linuxnet_interface_driver=linuxdrv) - fc = fake_client.FakeClient(LOG) - qc = quantum_connection.QuantumClientConnection(client=fc) - - self.net_man = quantum_manager.QuantumManager( - ipam_lib="nova.network.quantum.nova_ipam_lib", - q_conn=qc) - - def func(arg1, arg2): - pass - - def func2(arg1, arg2, arg3): - pass - - def func1(arg1): - pass - - self.net_man.driver.update_dhcp_hostfile_with_text = func - self.net_man.driver.restart_dhcp = func2 - self.net_man.driver.kill_dhcp = func1 - - # Tests seem to create some networks by default, which - # we don't want. So we delete them. - - ctx = context.RequestContext('user1', 'fake_project1').elevated() - for n in db.network_get_all(ctx): - db.network_delete_safe(ctx, n['id']) - - # Other unit tests (e.g., test_compute.py) have a nasty - # habit of of creating fixed IPs and not cleaning up, which - # can confuse these tests, so we remove all existing fixed - # ips before starting. - session = sql_session.get_session() - result = session.query(models.FixedIp).all() - with session.begin(): - for fip_ref in result: - session.delete(fip_ref) - - self.net_man.init_host() - - def _create_network(self, n): - ctx = context.RequestContext('user1', n['project_id']) - nwks = self.net_man.create_networks( - ctx, - label=n['label'], cidr=n['cidr'], - multi_host=n['multi_host'], - num_networks=1, network_size=256, - cidr_v6=n['cidr_v6'], - gateway=n['gateway'], - gateway_v6=n['gateway_v6'], bridge=None, - bridge_interface=None, dns1=n['dns1'], - project_id=n['project_id'], - priority=n['priority']) - n['uuid'] = nwks[0]['uuid'] - - -class QuantumAllocationTestCase(QuantumNovaTestCase): - def test_get_network_in_db(self): - context = self.mox.CreateMockAnything() - context.elevated().AndReturn('elevated') - self.mox.StubOutWithMock(db, 'network_get_by_uuid') - self.net_man.context = context - db.network_get_by_uuid('elevated', 'quantum_net_id').AndReturn( - {'uuid': 1}) - - self.mox.ReplayAll() - - network = self.net_man.get_network(context, ('quantum_net_id', - 'net_tenant_id')) - self.assertEquals(network['quantum_net_id'], 'quantum_net_id') - self.assertEquals(network['uuid'], 1) - - def test_get_network_not_in_db(self): - context = self.mox.CreateMockAnything() - context.elevated().AndReturn('elevated') - self.mox.StubOutWithMock(db, 'network_get_by_uuid') - self.net_man.context = context - db.network_get_by_uuid('elevated', 'quantum_net_id').AndReturn(None) - - self.mox.ReplayAll() - - network = self.net_man.get_network(context, ('quantum_net_id', - 'net_tenant_id')) - self.assertEquals(network['quantum_net_id'], 'quantum_net_id') - self.assertEquals(network['uuid'], 'quantum_net_id') - - -class QuantumDeallocationTestCase(QuantumNovaTestCase): - def test_deallocate_port(self): - quantum = self.mox.CreateMock( - quantum_connection.QuantumClientConnection) - quantum.get_port_by_attachment('q_tenant_id', 'net_id', - 'interface_id').AndReturn('port_id') - quantum.detach_and_delete_port('q_tenant_id', 'net_id', 'port_id') - self.net_man.q_conn = quantum - - self.mox.ReplayAll() - - self.net_man.deallocate_port('interface_id', 'net_id', 'q_tenant_id', - 'instance_id') - - def test_deallocate_port_logs_error(self): - quantum = self.mox.CreateMock( - quantum_connection.QuantumClientConnection) - quantum.get_port_by_attachment('q_tenant_id', 'net_id', - 'interface_id').AndRaise(Exception) - self.net_man.q_conn = quantum - - self.mox.StubOutWithMock(quantum_manager.LOG, 'exception') - quantum_manager.LOG.exception(mox.Regex(r'port deallocation failed')) - - self.mox.ReplayAll() - - self.net_man.deallocate_port('interface_id', 'net_id', 'q_tenant_id', - 'instance_id') - - def test_deallocate_ip_address(self): - ipam = self.mox.CreateMock(melange_ipam_lib.QuantumMelangeIPAMLib) - ipam.get_tenant_id_by_net_id('context', 'net_id', {'uuid': 1}, - 'project_id').AndReturn('ipam_tenant_id') - self.net_man.ipam = ipam - self.mox.ReplayAll() - self.net_man.deallocate_ip_address('context', 'net_id', 'project_id', - {'uuid': 1}, 'instance_id') - - def test_deallocate_ip_address_2(self): - ipam = self.mox.CreateMock(melange_ipam_lib.QuantumMelangeIPAMLib) - ipam.get_tenant_id_by_net_id('context', 'net_id', {'uuid': 1}, - 'project_id').AndRaise(Exception()) - self.net_man.ipam = ipam - - self.mox.StubOutWithMock(quantum_manager.LOG, 'exception') - quantum_manager.LOG.exception(mox.Regex(r'ipam deallocation failed')) - - self.mox.ReplayAll() - self.net_man.deallocate_ip_address('context', 'net_id', 'project_id', - {'uuid': 1}, 'instance_id') - - -class QuantumManagerTestCase(QuantumNovaTestCase): - def test_create_and_delete_nets(self): - self._create_nets() - self._delete_nets() - - def _create_nets(self): - for n in networks: - self._create_network(n) - - def _delete_nets(self): - for n in networks: - ctx = context.RequestContext('user1', n['project_id']) - self.net_man.delete_network(ctx, None, n['uuid']) - self.assertRaises(exception.NoNetworksFound, - db.network_get_all, ctx.elevated()) - - def _validate_nw_info(self, nw_info, expected_net_labels): - - self.assertEquals(len(nw_info), len(expected_net_labels)) - - ctx = context.RequestContext('user1', 'foo').elevated() - all_net_map = {} - for n in db.network_get_all(ctx): - all_net_map[n['label']] = n - - for i in range(0, len(nw_info)): - vif = nw_info[i] - net = all_net_map[expected_net_labels[i]] - - # simple test assumes that each starting prefix is unique - expected_v4_cidr_start = net['cidr'].split(".")[0].lower() - expected_v6_cidr_start = net['cidr_v6'].split(":")[0].lower() - - for subnet in vif['network']['subnets']: - addr = subnet['ips'][0]['address'] - if subnet['version'] == 4: - address_start = addr.split(".")[0].lower() - self.assertTrue(expected_v4_cidr_start, address_start) - else: - address_start = addr.split(":")[0].lower() - self.assertTrue(expected_v6_cidr_start, address_start) - - # confirm that there is a DHCP device on corresponding net - for l in expected_net_labels: - n = all_net_map[l] - tenant_id = (n['project_id'] or - FLAGS.quantum_default_tenant_id) - ports = self.net_man.q_conn.get_attached_ports( - tenant_id, n['uuid']) - self.assertEquals(len(ports), 2) # gw + instance VIF - - # make sure we aren't allowed to delete network with - # active port - self.assertRaises(exception.NetworkBusy, - self.net_man.delete_network, - ctx, None, n['uuid']) - - def _check_vifs(self, expect_num_vifs): - ctx = context.RequestContext('user1', "").elevated() - self.assertEqual(len(db.virtual_interface_get_all(ctx)), - expect_num_vifs) - - def _allocate_and_deallocate_instance(self, project_id, requested_networks, - expected_labels): - - ctx = context.RequestContext('user1', project_id) - self._check_vifs(0) - - instance_ref = db.instance_create(ctx, - {"project_id": project_id}) - - nw_info = self.net_man.allocate_for_instance(ctx.elevated(), - instance_id=instance_ref['id'], host="", - rxtx_factor=3, - project_id=project_id, - requested_networks=requested_networks) - - self._check_vifs(len(nw_info)) - - self._validate_nw_info(nw_info, expected_labels) - - nw_info = self.net_man.get_instance_nw_info(ctx, instance_ref['id'], - instance_ref['uuid'], - instance_ref['instance_type_id'], "", - project_id=project_id) - - self._check_vifs(len(nw_info)) - self._validate_nw_info(nw_info, expected_labels) - - port_net_pairs = [] - for vif in nw_info: - nid = vif['network']['id'] - pid = self.net_man.q_conn.get_port_by_attachment( - project_id, nid, vif['id']) - if pid is None: - pid = self.net_man.q_conn.get_port_by_attachment( - FLAGS.quantum_default_tenant_id, - nid, vif['id']) - self.assertTrue(pid is not None) - port_net_pairs.append((pid, nid)) - - self.net_man.deallocate_for_instance(ctx, - instance_id=instance_ref['id'], - project_id=project_id) - - for pid, nid in port_net_pairs: - self.assertRaises(quantum_client.QuantumNotFoundException, - self.net_man.q_conn.detach_and_delete_port, - project_id, nid, pid) - self.assertRaises(quantum_client.QuantumNotFoundException, - self.net_man.q_conn.detach_and_delete_port, - FLAGS.quantum_default_tenant_id, nid, pid) - - self._check_vifs(0) - - def test_allocate_and_deallocate_instance_static(self): - self._create_nets() - self._allocate_and_deallocate_instance("fake_project1", None, - ['public', 'project1-net1']) - self._delete_nets() - - def test_allocate_and_deallocate_instance_dynamic(self): - - self._create_nets() - project_id = "fake_project2" - ctx = context.RequestContext('user1', project_id) - all_valid_networks = self.net_man.ipam.get_project_and_global_net_ids( - ctx, project_id) - requested_networks = [(n[0], None) for n in all_valid_networks] - - self.net_man.validate_networks(ctx, requested_networks) - - label_map = {} - for n in db.network_get_all(ctx.elevated()): - label_map[n['uuid']] = n['label'] - expected_labels = [label_map[uid] for uid, _i in requested_networks] - - self._allocate_and_deallocate_instance(project_id, requested_networks, - expected_labels) - self._delete_nets() - - def test_validate_bad_network(self): - ctx = context.RequestContext('user1', 'fake_project1') - self.assertRaises(exception.NetworkNotFound, - self.net_man.validate_networks, ctx, [("", None)]) - - def test_create_net_external_uuid(self): - """Tests use case where network can be created directly via - Quantum API, then the UUID is passed in via nova-manage""" - project_id = "foo_project" - ctx = context.RequestContext('user1', project_id) - net_id = self.net_man.q_conn.create_network(project_id, 'net1') - self.net_man.create_networks( - ctx, - label='achtungbaby', - cidr="9.9.9.0/24", - multi_host=False, - num_networks=1, - network_size=256, - cidr_v6=None, - gateway="9.9.9.1", - gateway_v6=None, - bridge=None, - bridge_interface=None, - dns1="8.8.8.8", - project_id=project_id, - priority=9, - uuid=net_id) - net = db.network_get_by_uuid(ctx.elevated(), net_id) - self.assertTrue(net is not None) - self.assertEquals(net['uuid'], net_id) - - def test_create_net_external_uuid_and_host_is_set(self): - """Make sure network['host'] is set when creating a network via the - network manager""" - project_id = "foo_project" - ctx = context.RequestContext('user1', project_id) - net_id = self.net_man.q_conn.create_network(project_id, 'net2') - self.net_man.create_networks( - ctx, label='achtungbaby2', cidr="9.9.8.0/24", multi_host=False, - num_networks=1, network_size=256, cidr_v6=None, - gateway="9.9.8.1", gateway_v6=None, bridge=None, - bridge_interface=None, dns1="8.8.8.8", project_id=project_id, - priority=8, uuid=net_id) - net = db.network_get_by_uuid(ctx.elevated(), net_id) - self.assertTrue(net is not None) - self.assertEquals(net['uuid'], net_id) - self.assertTrue(net['host'] != None) - - -class QuantumNovaMACGenerationTestCase(QuantumNovaTestCase): - def test_local_mac_address_creation(self): - self.flags(use_melange_mac_generation=False) - fake_mac = "ab:cd:ef:ab:cd:ef" - self.stubs.Set(utils, "generate_mac_address", - lambda: fake_mac) - project_id = "fake_project1" - ctx = context.RequestContext('user1', project_id) - self._create_network(networks[0]) - - all_valid_networks = self.net_man.ipam.get_project_and_global_net_ids( - ctx, project_id) - requested_networks = [(n[0], None) for n in all_valid_networks] - - instance_ref = db.api.instance_create(ctx, - {"project_id": project_id}) - nw_info = self.net_man.allocate_for_instance(ctx, - instance_id=instance_ref['id'], host="", - rxtx_factor=3, - project_id=project_id, - requested_networks=requested_networks) - self.assertEqual(nw_info[0]['address'], fake_mac) - - def test_melange_mac_address_creation(self): - self.flags(use_melange_mac_generation=True) - fake_mac = "ab:cd:ef:ab:cd:ef" - self.stubs.Set(melange_connection.MelangeConnection, "create_vif", - lambda w, x, y, z: fake_mac) - project_id = "fake_project1" - ctx = context.RequestContext('user1', project_id) - self._create_network(networks[0]) - - all_valid_networks = self.net_man.ipam.get_project_and_global_net_ids( - ctx, project_id) - requested_networks = [(n[0], None) for n in all_valid_networks] - - instance_ref = db.api.instance_create(ctx, - {"project_id": project_id}) - nw_info = self.net_man.allocate_for_instance(ctx, - instance_id=instance_ref['id'], host="", - rxtx_factor=3, - project_id=project_id, - requested_networks=requested_networks) - self.assertEqual(nw_info[0]['address'], fake_mac) - - -class QuantumNovaPortSecurityTestCase(QuantumNovaTestCase): - def test_port_security(self): - self.flags(use_melange_mac_generation=True) - self.flags(quantum_use_port_security=True) - fake_mac = "ab:cd:ef:ab:cd:ef" - self.stubs.Set(melange_connection.MelangeConnection, "create_vif", - lambda w, x, y, z: fake_mac) - project_id = "fake_project1" - ctx = context.RequestContext('user1', project_id) - self._create_network(networks[0]) - - all_valid_networks = self.net_man.ipam.get_project_and_global_net_ids( - ctx, project_id) - requested_networks = [(n[0], None) for n in all_valid_networks] - - instance_ref = db.api.instance_create(ctx, - {"project_id": project_id}) - oldfunc = self.net_man.q_conn.create_and_attach_port - - # Make sure we get the appropriate mac set in allowed_address_pairs - # if port security is enabled. - def _instrumented_create_and_attach_port(tenant_id, net_id, - interface_id, **kwargs): - self.assertTrue('allowed_address_pairs' in kwargs.keys()) - pairs = kwargs['allowed_address_pairs'] - self.assertTrue(pairs[0]['mac_address'] == fake_mac) - self.net_man.q_conn.create_and_attach_port = oldfunc - return oldfunc(tenant_id, net_id, interface_id, **kwargs) - _port_attach = _instrumented_create_and_attach_port - self.net_man.q_conn.create_and_attach_port = _port_attach - nw_info = self.net_man.allocate_for_instance(ctx, - instance_id=instance_ref['id'], host="", - rxtx_factor=3, - project_id=project_id, - requested_networks=requested_networks) - self.assertEqual(nw_info[0]['address'], fake_mac) - - def test_port_security_negative(self): - self.flags(use_melange_mac_generation=True) - self.flags(quantum_use_port_security=False) - fake_mac = "ab:cd:ef:ab:cd:ef" - self.stubs.Set(melange_connection.MelangeConnection, "create_vif", - lambda w, x, y, z: fake_mac) - project_id = "fake_project1" - ctx = context.RequestContext('user1', project_id) - self._create_network(networks[0]) - - all_valid_networks = self.net_man.ipam.get_project_and_global_net_ids( - ctx, project_id) - requested_networks = [(n[0], None) for n in all_valid_networks] - - instance_ref = db.api.instance_create(ctx, - {"project_id": project_id}) - oldfunc = self.net_man.q_conn.create_and_attach_port - - # Make sure no pairs are passed in if port security is turned off - def _instrumented_create_and_attach_port(tenant_id, net_id, - interface_id, **kwargs): - self.assertTrue('allowed_address_pairs' in kwargs.keys()) - pairs = kwargs['allowed_address_pairs'] - self.assertTrue(len(pairs) == 0) - self.net_man.q_conn.create_and_attach_port = oldfunc - return oldfunc(tenant_id, net_id, interface_id, **kwargs) - _port_attach = _instrumented_create_and_attach_port - self.net_man.q_conn.create_and_attach_port = _port_attach - nw_info = self.net_man.allocate_for_instance(ctx, - instance_id=instance_ref['id'], host="", - rxtx_factor=3, - project_id=project_id, - requested_networks=requested_networks) - self.assertEqual(nw_info[0]['address'], fake_mac) - - -class QuantumMelangeTestCase(test.TestCase): - def setUp(self): - super(QuantumMelangeTestCase, self).setUp() - - fc = fake_client.FakeClient(LOG) - qc = quantum_connection.QuantumClientConnection(client=fc) - - self.net_man = quantum_manager.QuantumManager( - ipam_lib="nova.network.quantum.nova_ipam_lib", - q_conn=qc) - - def test_get_instance_uuids_by_ip_filter(self): - fake_context = context.RequestContext('user', 'project') - address = '1.2.3.4' - filters = {'ip': address} - - self.net_man.ipam = self.mox.CreateMockAnything() - self.net_man.ipam.get_instance_ids_by_ip_address(fake_context, - address).AndReturn(['instance_id']) - - instance = self.mox.CreateMockAnything() - instance.uuid = 'instance_uuid' - - self.mox.StubOutWithMock(db, 'instance_get') - db.instance_get(fake_context, 'instance_id').AndReturn(instance) - - self.mox.ReplayAll() - - uuids = self.net_man.get_instance_uuids_by_ip_filter(fake_context, - filters) - self.assertEquals(uuids, [{'instance_uuid':'instance_uuid'}]) diff --git a/nova/tests/test_melange_ipam_lib.py b/nova/tests/test_melange_ipam_lib.py deleted file mode 100644 index d752a5dee..000000000 --- a/nova/tests/test_melange_ipam_lib.py +++ /dev/null @@ -1,243 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 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. - -from nova import db -from nova import flags -from nova.network.quantum import melange_connection -from nova.network.quantum import melange_ipam_lib -from nova import test - -FLAGS = flags.FLAGS - - -class MelangeIpamLibTestCase(test.TestCase): - def setUp(self): - super(MelangeIpamLibTestCase, self).setUp() - self.m_conn = self.mox.CreateMock(melange_connection.MelangeConnection) - self.ipam = melange_ipam_lib.QuantumMelangeIPAMLib() - self.ipam.m_conn = self.m_conn - - def _block_list(self, id='id', cidr='cidr', network_id='network_id'): - return {'ip_blocks': [{'id': id, - 'cidr': cidr, - 'network_id': network_id}]} - - def test_allocate_fixed_ips_extracts_address(self): - self.m_conn.allocate_ip('quantum_net_id', 'network_tenant_id', - 'vif_ref_uuid', 'project_id', - 'vif_ref_address').AndReturn( - [{'address': 'ip_address'}]) - - self.mox.ReplayAll() - - ips = self.ipam.allocate_fixed_ips('context', - 'project_id', - 'quantum_net_id', - 'network_tenant_id', - {'uuid': 'vif_ref_uuid', - 'address': 'vif_ref_address'}) - self.assertEqual(ips[0], 'ip_address') - - def test_delete_subnets_by_net_id_deletes_block(self): - context = self.mox.CreateMockAnything() - context.elevated().AndReturn('elevated') - - self.m_conn.get_blocks('project_id').AndReturn( - self._block_list(id='block_id')) - self.m_conn.delete_block('block_id', 'project_id') - - self.mox.StubOutWithMock(db, 'network_get_by_uuid') - db.network_get_by_uuid('elevated', 'network_id').AndReturn( - {'id': 'network_id'}) - - self.mox.StubOutWithMock(db, 'network_delete_safe') - db.network_delete_safe(context, 'network_id') - - self.mox.ReplayAll() - self.ipam.delete_subnets_by_net_id(context, 'network_id', 'project_id') - - def test_get_networks_by_tenant_gets_all_networks(self): - block_list = self._block_list(network_id='net_1') - block_list['ip_blocks'] += self._block_list( - network_id='net_2')['ip_blocks'] - - self.m_conn.get_blocks('tenant_id').AndReturn(block_list) - - self.mox.StubOutWithMock(db, 'network_get_by_uuid') - db.network_get_by_uuid('admin_context', 'net_1').AndReturn('network1') - db.network_get_by_uuid('admin_context', 'net_2').AndReturn('network2') - - self.mox.ReplayAll() - values = self.ipam.get_networks_by_tenant('admin_context', 'tenant_id') - self.assertEquals(values, ['network1', 'network2']) - - def test_get_global_networks(self): - FLAGS.quantum_default_tenant_id = 'quantum_default_tenant_id' - self.mox.StubOutWithMock(self.ipam, 'get_networks_by_tenant') - self.ipam.get_networks_by_tenant('admin_context', - 'quantum_default_tenant_id') - - self.mox.ReplayAll() - - self.ipam.get_global_networks('admin_context') - - def test_get_project_networks(self): - context = self.mox.CreateMockAnything() - context.elevated().AndReturn('elevated') - - networks = [{'project_id': 1}, {'project_id': None}] - - self.mox.StubOutWithMock(db, 'network_get_all') - db.network_get_all('elevated').AndReturn(networks) - - self.mox.ReplayAll() - values = self.ipam.get_project_networks(context) - self.assertEquals(values, [networks[0]]) - - def test_get_project_and_global_net_ids__by_priority(self): - context = self.mox.CreateMockAnything() - context.elevated().AndReturn('elevated') - - FLAGS.quantum_default_tenant_id = 'default_tenant_id' - - net1 = {'uuid': 'net1_uuid', 'priority': 'net1_priority'} - net2 = {'uuid': 'net2_uuid', 'priority': 'net2_priority'} - - self.mox.StubOutWithMock(self.ipam, 'get_networks_by_tenant') - self.ipam.get_networks_by_tenant('elevated', - 'project_id').AndReturn([net1]) - self.ipam.get_networks_by_tenant('elevated', - 'default_tenant_id').AndReturn([net2]) - self.mox.ReplayAll() - self.ipam.get_project_and_global_net_ids(context, 'project_id') - - def test_get_tenant_id_by_net_id_returns_id(self): - FLAGS.quantum_default_tenant_id = 'qdti' - - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'qdti').AndReturn({}) - self.mox.ReplayAll() - value = self.ipam.get_tenant_id_by_net_id('context', 'net_id', - 'vif_id', 'project_id') - self.assertEqual(value, 'qdti') - - def test_get_tenant_id_by_net_id_returns_none_if_none_found(self): - FLAGS.quantum_default_tenant_id = 'qdti' - - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'qdti').AndRaise(KeyError()) - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'project_id').AndRaise(KeyError()) - self.m_conn.get_allocated_ips('net_id', 'vif_id', - None).AndRaise(KeyError()) - self.mox.ReplayAll() - value = self.ipam.get_tenant_id_by_net_id('context', 'net_id', - 'vif_id', 'project_id') - self.assertEqual(value, None) - - def test_get_subnets_by_net_id(self): - ips = [{'ip_block': {'network_id': 'network_id', - 'id': 'id', - 'cidr': 'cidr', - 'gateway': 'gateway', - 'broadcast': 'broadcast', - 'netmask': 'netmask', - 'dns1': 'dns1', - 'dns2': 'dns2'}, - 'version': 4}] - - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'tenant_id').AndReturn(ips) - - self.mox.ReplayAll() - value = self.ipam.get_subnets_by_net_id('context', 'tenant_id', - 'net_id', 'vif_id') - self.assertEquals(value[0]['cidr'], 'cidr') - - def test_get_routes_by_ip_block(self): - self.m_conn.get_routes('block_id', 'project_id') - self.mox.ReplayAll() - self.ipam.get_routes_by_ip_block('context', 'block_id', 'project_id') - - def test_get_v4_ips_by_interface(self): - self.mox.StubOutWithMock(self.ipam, '_get_ips_by_interface') - self.ipam._get_ips_by_interface('context', 'net_id', 'vif_id', - 'project_id', 4) - self.mox.ReplayAll() - self.ipam.get_v4_ips_by_interface('context', 'net_id', 'vif_id', - 'project_id') - - def test_get_v6_ips_by_interface(self): - self.mox.StubOutWithMock(self.ipam, '_get_ips_by_interface') - self.ipam._get_ips_by_interface('context', 'net_id', 'vif_id', - 'project_id', 6) - self.mox.ReplayAll() - self.ipam.get_v6_ips_by_interface('context', 'net_id', 'vif_id', - 'project_id') - - def test_get_ips_by_interface(self): - ips = [{'address': '10.10.10.10'}, {'address': '2001::CAFE'}] - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'tenant_id').AndReturn(ips) - self.m_conn.get_allocated_ips('net_id', 'vif_id', - 'tenant_id').AndReturn(ips) - self.mox.ReplayAll() - values = self.ipam._get_ips_by_interface('context', 'net_id', 'vif_id', - 'tenant_id', 4) - self.assertEquals(values, ["10.10.10.10"]) - values = self.ipam._get_ips_by_interface('context', 'net_id', 'vif_id', - 'tenant_id', 6) - self.assertEquals(values, ["2001::CAFE"]) - - def test_get_instance_ids_by_ip_address(self): - ips = [{'used_by_device': 'some_vif_uuid'}] - self.m_conn.get_allocated_ips_by_address('ip').AndReturn(ips) - - self.mox.ReplayAll() - uuid = self.ipam.get_instance_ids_by_ip_address('context', 'ip') - self.assertEqual(uuid, ['some_vif_uuid']) - - def test_verify_subnet_exists(self): - blocks = {'ip_blocks': [{'network_id': 'quantum_net_id'}]} - self.m_conn.get_blocks('tenant_id').AndReturn(blocks) - self.mox.ReplayAll() - value = self.ipam.verify_subnet_exists('context', 'tenant_id', - 'quantum_net_id') - self.assertEquals(value, True) - - def test_deallocate_ips_by_vif(self): - self.m_conn.deallocate_ips('net_id', 'uuid', 'tenant_id') - self.mox.ReplayAll() - self.ipam.deallocate_ips_by_vif('context', 'tenant_id', 'net_id', - {'uuid': 'uuid'}) - - def test_get_allocated_ips(self): - ips = [{'address': 'ip_address', 'interface_id': 'interface_id'}] - self.m_conn.get_allocated_ips_for_network('subnet_id', - 'project_id').AndReturn(ips) - self.mox.ReplayAll() - self.ipam.get_allocated_ips('context', 'subnet_id', 'project_id') - - def test_create_vif(self): - self.m_conn.create_vif('vif_id', 'instance_id', 'project_id') - self.mox.ReplayAll() - self.ipam.create_vif('vif_id', 'instance_id', 'project_id') - - def test_get_floating_ips_by_fixed_address(self): - value = self.ipam.get_floating_ips_by_fixed_address('context', - 'fixed_address') - self.assertEquals(value, []) |
