summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordanwent@gmail.com <>2011-08-28 19:13:02 -0700
committerdanwent@gmail.com <>2011-08-28 19:13:02 -0700
commit716303049eaee59841ca4679d73ecb4e5be52cfd (patch)
tree4c04d1c1d5bcf6c28437957b38e8eb5e50f9134e
parent822d92ed1f6a5f2f0951c5e43be6ce0c8fb75e65 (diff)
add doc-strings for all major modules
-rw-r--r--nova/network/quantum/manager.py97
-rw-r--r--nova/network/quantum/melange_ipam_lib.py81
-rw-r--r--nova/network/quantum/nova_ipam_lib.py85
-rw-r--r--nova/network/quantum/quantum_connection.py28
4 files changed, 226 insertions, 65 deletions
diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py
index fb13a8496..a002a3d7b 100644
--- a/nova/network/quantum/manager.py
+++ b/nova/network/quantum/manager.py
@@ -35,8 +35,27 @@ flags.DEFINE_string('quantum_ipam_lib',
class QuantumManager(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.
+
+ Currently, the QuantumManager does NOT support any of the 'gateway'
+ functionality implemented by the Nova VlanManager, including:
+ * floating IPs
+ * DHCP
+ * NAT gateway
+
+ Support for these capabilities are targted for future releases.
+ """
def __init__(self, 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 FLAGS.fake_network:
self.q_conn = fake.FakeQuantumClientConnection()
@@ -53,6 +72,17 @@ class QuantumManager(manager.FlatManager):
network_size, cidr_v6, 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.
+ """
if num_networks != 1:
raise Exception("QuantumManager requires that only one"
" network is created per call")
@@ -74,27 +104,46 @@ class QuantumManager(manager.FlatManager):
priority, cidr, gateway_v6, cidr_v6, dns1, dns2)
def delete_network(self, context, fixed_range):
+ """ Lookup network by IPv4 cidr, delete both the IPAM
+ subnet and the corresponding Quantum network.
+ """
project_id = context.project_id
quantum_net_id = self.ipam.get_network_id_by_cidr(
context, fixed_range, project_id)
self.ipam.delete_subnets_by_net_id(context, quantum_net_id,
project_id)
- try:
- q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
- self.q_conn.delete_network(q_tenant_id, quantum_net_id)
- except Exception, e:
- raise Exception("Unable to delete Quantum Network with "
- "fixed_range = %s (ports still in use?)." % fixed_range)
+ q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
+ self.q_conn.delete_network(q_tenant_id, quantum_net_id)
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.pop('instance_id')
instance_type_id = kwargs['instance_type_id']
host = kwargs.pop('host')
project_id = kwargs.pop('project_id')
LOG.debug(_("network allocations for instance %s"), instance_id)
- # if using the create-server-networks extension, 'requested_networks'
- # will be defined, otherwise, just grab relevant nets from IPAM
requested_networks = kwargs.get('requested_networks')
if requested_networks:
@@ -116,10 +165,12 @@ class QuantumManager(manager.FlatManager):
# updating the virtual_interfaces table to use UUIDs would
# be one solution, but this would require significant work
# elsewhere.
- network_ref = db.network_get_by_uuid(context, quantum_net_id)
+ admin_context = context.elevated()
+ network_ref = db.network_get_by_uuid(admin_context,
+ quantum_net_id)
vif_rec = manager.FlatManager.add_virtual_interface(self,
- context, instance_id, network_ref['id'])
+ context, instance_id, network_ref['id'])
# talk to Quantum API to create and attach port.
q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
@@ -133,6 +184,18 @@ class QuantumManager(manager.FlatManager):
def get_instance_nw_info(self, context, instance_id,
instance_type_id, host):
+ """ 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.
+ """
network_info = []
instance = db.instance_get(context, instance_id)
project_id = instance.project_id
@@ -202,6 +265,11 @@ class QuantumManager(manager.FlatManager):
return network_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)
@@ -234,11 +302,12 @@ class QuantumManager(manager.FlatManager):
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
- # 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.
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
diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py
index e2e09f139..7b7baf281 100644
--- a/nova/network/quantum/melange_ipam_lib.py
+++ b/nova/network/quantum/melange_ipam_lib.py
@@ -32,43 +32,54 @@ def get_ipam_lib(net_man):
class QuantumMelangeIPAMLib:
+ """ 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_v6=None, cidr_v6=None,
dns1=None, dns2=None):
- tenant_id = project_id or FLAGS.quantum_default_tenant_id
- if cidr:
- self.m_conn.create_block(quantum_net_id, cidr,
+ """ Contact Melange and create a subnet for any non-NULL
+ IPv4 or IPv6 subnets.
+
+ Also create a 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,
dns1=dns1, dns2=dns2)
- if cidr_v6:
- self.m_conn.create_block(quantum_net_id, cidr_v6,
+ if cidr_v6:
+ self.m_conn.create_block(quantum_net_id, cidr_v6,
project_id=tenant_id,
dns1=dns1, dns2=dns2)
- # create a entry in the network table, even though
- # most data is stored in melange. This is used to
- # store data not kept by melange (e.g., priority)
- # and to 'fake' other parts of nova (e.g., the API)
- # until we get get all accesses to be via the
- # network manager API.
- net = {"uuid": quantum_net_id,
+ net = {"uuid": quantum_net_id,
"project_id": project_id,
"priority": priority,
"label": label}
- network = self.db.network_create_safe(context, net)
+ network = self.db.network_create_safe(context, net)
def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref):
+ """ Pass call to allocate fixed IP on to Melange"""
tenant_id = project_id or FLAGS.quantum_default_tenant_id
self.m_conn.allocate_ip(quantum_net_id,
vif_ref['uuid'], project_id=tenant_id,
mac_address=vif_ref['address'])
def get_network_id_by_cidr(self, context, cidr, project_id):
+ """ Find the Quantum UUID associated with a IPv4 CIDR
+ address for the specified tenant.
+ """
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']:
@@ -77,6 +88,9 @@ class QuantumMelangeIPAMLib:
raise Exception("No network found for cidr %s" % cidr)
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)
@@ -88,9 +102,11 @@ class QuantumMelangeIPAMLib:
if network is not None:
db.network_delete_safe(context, network['id'])
- # get all networks with this project_id, as well as all networks
- # where the project-id is not set (these are shared networks)
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'.
+ """
admin_context = context.elevated()
id_proj_map = {}
if project_id is None:
@@ -115,12 +131,16 @@ class QuantumMelangeIPAMLib:
return sorted(id_priority_map.items(),
key=lambda x: id_priority_map[x[0]])
- # FIXME: (danwent) Melange actually returns the subnet info
- # when we query for a particular interface. we may want to
- # reworks the ipam_manager python API to let us take advantage of
- # this, as right now we have to get all blocks and cycle through
- # them.
def get_subnets_by_net_id(self, context, project_id, net_id):
+ """ Returns information about the IPv4 and IPv6 subnets
+ associated with a Quantum Network UUID.
+ """
+
+ # FIXME: (danwent) Melange actually returns the subnet info
+ # when we query for a particular interface. we may want to
+ # reworks the ipam_manager python API to let us take advantage of
+ # this, as right now we have to get all blocks and cycle through
+ # them.
subnet_v4 = None
subnet_v6 = None
tenant_id = project_id or FLAGS.quantum_default_tenant_id
@@ -142,26 +162,41 @@ class QuantumMelangeIPAMLib:
return (subnet_v4, subnet_v6)
def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id):
- return self.get_ips_by_interface(context, net_id, vif_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):
- return self.get_ips_by_interface(context, net_id, vif_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,
+ 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 IPNetwork(ip['address']).version == ip_version]
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.
+ """
tenant_id = project_id or FLAGS.quantum_default_tenant_id
v4_subnet, v6_subnet = self.get_subnets_by_net_id(context, tenant_id,
quantum_net_id)
return v4_subnet is not None
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)
diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py
index 6e7e5d244..ce7d3efcb 100644
--- a/nova/network/quantum/nova_ipam_lib.py
+++ b/nova/network/quantum/nova_ipam_lib.py
@@ -37,52 +37,69 @@ def get_ipam_lib(net_man):
class QuantumNovaIPAMLib:
+ """ 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 create_subnet(self, context, label, tenant_id,
quantum_net_id, priority, cidr=None,
gateway_v6=None, cidr_v6=None,
dns1=None, dns2=None):
- admin_context = context.elevated()
- subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1]))))
- networks = manager.FlatManager.create_networks(self.net_manager,
+ """ Re-use the basic FlatManager create_networks method to
+ initialize the networks and fixed_ips tables in Nova DB.
+
+ Also stores a few more fields in the networks table that
+ are needed by Quantum but not the FlatManager.
+ """
+ admin_context = context.elevated()
+ subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1]))))
+ networks = manager.FlatManager.create_networks(self.net_manager,
admin_context, label, cidr,
False, 1, subnet_size, cidr_v6,
gateway_v6, quantum_net_id, None, dns1, dns2)
- if len(networks) != 1:
- raise Exception("Error creating network entry")
+ if len(networks) != 1:
+ raise Exception("Error creating network entry")
- # now grab the network and update additional fields
- network = networks[0]
- net = {"project_id": tenant_id,
+ network = networks[0]
+ net = {"project_id": tenant_id,
"priority": priority,
"uuid": quantum_net_id}
- db.network_update(admin_context, network['id'], net)
+ db.network_update(admin_context, network['id'], net)
def get_network_id_by_cidr(self, context, cidr, project_id):
- admin_context = context.elevated()
- network = db.network_get_by_cidr(admin_context, cidr)
- if not network:
- raise Exception("No network with fixed_range = %s" \
- % fixed_range)
- return network['uuid']
+ """ Grabs Quantum network UUID based on IPv4 CIDR. """
+ admin_context = context.elevated()
+ network = db.network_get_by_cidr(admin_context, cidr)
+ if not network:
+ raise Exception("No network with fixed_range = %s" % fixed_range)
+ return network['uuid']
def delete_subnets_by_net_id(self, context, net_id, project_id):
- admin_context = context.elevated()
- network = db.network_get_by_uuid(admin_context, net_id)
- if not network:
- raise Exception("No network with net_id = %s" % net_id)
- manager.FlatManager.delete_network(self.net_manager,
+ """ Deletes a network based on Quantum UUID. Uses FlatManager
+ delete_network to avoid duplication.
+ """
+ admin_context = context.elevated()
+ network = db.network_get_by_uuid(admin_context, net_id)
+ if not network:
+ raise Exception("No network with net_id = %s" % net_id)
+ manager.FlatManager.delete_network(self.net_manager,
admin_context, network['cidr'],
require_disassociated=False)
def get_project_and_global_net_ids(self, context, project_id):
-
- # get all networks with this project_id, as well as all networks
- # where the project-id is not set (these are shared networks)
+ """ Fetches all networks associated with this project, or
+ that are "global" (i.e., have no project set).
+ Returns list sorted by 'priority'.
+ """
admin_context = context.elevated()
networks = db.project_get_networks(admin_context, project_id, False)
networks.extend(db.project_get_networks(admin_context, None, False))
@@ -95,6 +112,8 @@ class QuantumNovaIPAMLib:
return sorted(net_list, key=lambda x: id_priority_map[x[0]])
def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec):
+ """ Allocates a single fixed IPv4 address for a virtual interface.
+ """
admin_context = context.elevated()
network = db.network_get_by_uuid(admin_context, quantum_net_id)
if network['cidr']:
@@ -106,6 +125,9 @@ class QuantumNovaIPAMLib:
db.fixed_ip_update(admin_context, address, values)
def get_subnets_by_net_id(self, context, tenant_id, net_id):
+ """ 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_data_v4 = {
'network_id': n['uuid'],
@@ -126,12 +148,18 @@ class QuantumNovaIPAMLib:
return (subnet_data_v4, subnet_data_v6)
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.
+ """
vif_rec = db.virtual_interface_get_by_uuid(context, vif_id)
fixed_ips = db.fixed_ip_get_by_virtual_interface(context,
vif_rec['id'])
return [f['address'] for f 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)
@@ -143,10 +171,17 @@ class QuantumNovaIPAMLib:
return []
def verify_subnet_exists(self, context, tenant_id, quantum_net_id):
+ """ Confirms that a subnet exists that is associated with the
+ specified Quantum Network UUID. Raises an exception if no
+ such subnet exists.
+ """
admin_context = context.elevated()
- network = db.network_get_by_uuid(admin_context, quantum_net_id)
+ db.network_get_by_uuid(admin_context, quantum_net_id)
def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref):
+ """ Deallocate all fixed IPs associated with the specified
+ virtual interface.
+ """
try:
admin_context = context.elevated()
fixed_ips = db.fixed_ip_get_by_virtual_interface(admin_context,
@@ -156,5 +191,5 @@ class QuantumNovaIPAMLib:
{'allocated': False,
'virtual_interface_id': None})
except exception.FixedIpNotFoundForInstance:
- LOG.error(_('Failed to deallocate fixed IP for vif %s' % \
+ LOG.error(_('No fixed IPs to deallocate for vif %s' % \
vif_ref['id']))
diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py
index a13867af2..bd3592c2c 100644
--- a/nova/network/quantum/quantum_connection.py
+++ b/nova/network/quantum/quantum_connection.py
@@ -38,31 +38,49 @@ flags.DEFINE_string('quantum_default_tenant_id',
class QuantumClientConnection:
+ """ 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):
+ """ Initialize Quantum client class based on flags. """
self.client = quantum_client.Client(FLAGS.quantum_connection_host,
FLAGS.quantum_connection_port,
format="json",
logger=LOG)
def create_network(self, tenant_id, network_name):
+ """ Create network using specified name, return Quantum
+ network UUID.
+ """
data = {'network': {'name': network_name}}
resdict = self.client.create_network(data, tenant=tenant_id)
return resdict["network"]["id"]
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)
except:
- # FIXME: client lib should expose more granular exceptions
+ # FIXME: (danwent) client lib should expose granular exceptions
# so we can confirm we're getting a 404 and not some other error
return False
return True
def create_and_attach_port(self, tenant_id, net_id, interface_id):
+ """ 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 %s to net %s for %s" % \
(interface_id, net_id, tenant_id))
port_data = {'port': {'state': 'ACTIVE'}}
@@ -74,15 +92,19 @@ class QuantumClientConnection:
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 %s on net %s for %s" % \
(port_id, net_id, tenant_id))
self.client.detach_resource(net_id, port_id, tenant=tenant_id)
self.client.delete_port(net_id, port_id, tenant=tenant_id)
- # FIXME: (danwent) this will be inefficient until API implements querying
def get_port_by_attachment(self, tenant_id, attachment_id):
-
+ """ Given a tenant, search for the Quantum network and port
+ UUID that has the specified interface-id attachment.
+ """
+ # FIXME: (danwent) this will be inefficient until the Quantum
+ # API implements querying a port by the interface-id
net_list_resdict = self.client.list_networks(tenant=tenant_id)
for n in net_list_resdict["networks"]:
net_id = n['id']