add doc-strings for all major modules

This commit is contained in:
danwent@gmail.com
2011-08-28 19:13:02 -07:00
parent 822d92ed1f
commit 716303049e
4 changed files with 226 additions and 65 deletions
+83 -14
View File
@@ -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
+58 -23
View File
@@ -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)
+60 -25
View File
@@ -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']))
+25 -3
View File
@@ -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']