summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-08-12 02:15:37 +0000
committerGerrit Code Review <review@openstack.org>2012-08-12 02:15:37 +0000
commit480f4e2a3a2992acf25628eddfc6f87379cf2445 (patch)
tree119af3f3c3d86d04346fa6e5444291a1783b6f6a /nova
parent0effb5ce1dec1ebb0dfc748ac7f67d771c1281a7 (diff)
parent357ffa7d3aa2124d9d7697a4bd492b7819646ba9 (diff)
downloadnova-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.py2
-rw-r--r--nova/network/manager.py2
-rw-r--r--nova/network/nova_ipam_lib.py101
-rw-r--r--nova/network/quantum/__init__.py16
-rw-r--r--nova/network/quantum/client.py312
-rw-r--r--nova/network/quantum/fake_client.py171
-rw-r--r--nova/network/quantum/manager.py762
-rw-r--r--nova/network/quantum/melange_connection.py202
-rw-r--r--nova/network/quantum/melange_ipam_lib.py261
-rw-r--r--nova/network/quantum/quantum_connection.py159
-rw-r--r--nova/network/sg.py (renamed from nova/network/quantum/sg.py)0
-rw-r--r--nova/tests/fake_network.py2
-rw-r--r--nova/tests/network/test_quantum.py647
-rw-r--r--nova/tests/test_melange_ipam_lib.py243
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, [])