Add DHCP support to the QuantumManager and break apart dhcp/gateway

This introduces a new flag "quantum_use_dhcp=<boolean>" which indicates whether
or not to enable dhcp for all of the networks.  If it is set then we start
dnsmasq (and provide it with the IP/MACs from Melange) similar to how this was
done in linux_net before.

Prior to this if you enabled dhcp then you would also get a gateway device..
some people may not want that so we now require that you specify the gateway
when creating the network in order to end up with a device that will act as a
gateway.  If you're using Melange IPAM and you don't specify the gateway you
still end up with one because it doesn't allow you to not have one.  This lays
the groundwork for the option of not having one in the future, at least :)

Also, fix quantum/melange ipam interaction

We now query for the subnets by net_id/vif_id instead of searching through all
the blocks to find the right one.  Both of the allocate and deallocate for
instance calls are now using the vif_id -> network_id mapping instead of
searching the quantum networks.  get_port_by_attachment was also changed to
take a net_id so that we don't have to search through all of the quantum
networks to find the corresponding port.

Change-Id: I6a84da35237b6c5f5cdee91ada92642103439a97
This commit is contained in:
Brad Hall 2011-11-04 20:11:53 -07:00
parent f89f27b184
commit 38172d5587
14 changed files with 481 additions and 137 deletions

View File

@ -90,7 +90,8 @@ def init_leases(network_id):
"""Get the list of hosts for a network."""
ctxt = context.get_admin_context()
network_ref = db.network_get(ctxt, network_id)
return linux_net.get_dhcp_leases(ctxt, network_ref)
network_manager = utils.import_object(FLAGS.network_manager)
return network_manager.get_dhcp_leases(ctxt, network_ref)
def main():

View File

@ -727,6 +727,7 @@ class NetworkCommands(object):
@args('--vpn', dest="vpn_start", help='vpn start')
@args('--fixed_range_v6', dest="fixed_range_v6",
help='IPv6 subnet (ex: fe80::/64')
@args('--gateway', dest="gateway", help='gateway')
@args('--gateway_v6', dest="gateway_v6", help='ipv6 gateway')
@args('--bridge', dest="bridge",
metavar='<bridge>',
@ -746,9 +747,10 @@ class NetworkCommands(object):
help='Network interface priority')
def create(self, label=None, fixed_range_v4=None, num_networks=None,
network_size=None, multi_host=None, vlan_start=None,
vpn_start=None, fixed_range_v6=None, gateway_v6=None,
bridge=None, bridge_interface=None, dns1=None, dns2=None,
project_id=None, priority=None, uuid=None):
vpn_start=None, fixed_range_v6=None, gateway=None,
gateway_v6=None, bridge=None, bridge_interface=None,
dns1=None, dns2=None, project_id=None, priority=None,
uuid=None):
"""Creates fixed ips for host by range"""
# check for certain required inputs
@ -811,6 +813,7 @@ class NetworkCommands(object):
vlan_start=int(vlan_start),
vpn_start=int(vpn_start),
cidr_v6=fixed_range_v6,
gateway=gateway,
gateway_v6=gateway_v6,
bridge=bridge,
bridge_interface=bridge_interface,

View File

@ -595,11 +595,29 @@ def release_dhcp(dev, address, mac_address):
utils.execute('dhcp_release', dev, address, mac_address, run_as_root=True)
def update_dhcp(context, dev, network_ref):
conffile = _dhcp_file(dev, 'conf')
with open(conffile, 'w') as f:
f.write(get_dhcp_hosts(context, network_ref))
restart_dhcp(dev, network_ref)
def update_dhcp_hostfile_with_text(dev, hosts_text):
conffile = _dhcp_file(dev, 'conf')
with open(conffile, 'w') as f:
f.write(hosts_text)
def kill_dhcp(dev):
pid = _dnsmasq_pid_for(dev)
_execute('kill', '-9', pid, run_as_root=True)
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
@utils.synchronized('dnsmasq_start')
def update_dhcp(context, dev, network_ref):
def restart_dhcp(dev, network_ref):
"""(Re)starts a dnsmasq server for a given network.
If a dnsmasq instance is already running then send a HUP
@ -607,8 +625,6 @@ def update_dhcp(context, dev, network_ref):
"""
conffile = _dhcp_file(dev, 'conf')
with open(conffile, 'w') as f:
f.write(get_dhcp_hosts(context, network_ref))
if FLAGS.use_single_default_gateway:
optsfile = _dhcp_file(dev, 'opts')
@ -625,7 +641,9 @@ def update_dhcp(context, dev, network_ref):
if pid:
out, _err = _execute('cat', '/proc/%d/cmdline' % pid,
check_exit_code=False)
if conffile in out:
# Using symlinks can cause problems here so just compare the name
# of the file itself
if conffile.split("/")[-1] in out:
try:
_execute('kill', '-HUP', pid, run_as_root=True)
return
@ -830,8 +848,8 @@ def _ip_bridge_cmd(action, params, device):
# act as gateway/dhcp/vpn/etc. endpoints not VM interfaces.
def plug(network, mac_address):
return interface_driver.plug(network, mac_address)
def plug(network, mac_address, gateway=True):
return interface_driver.plug(network, mac_address, gateway)
def unplug(network):
@ -862,7 +880,7 @@ class LinuxNetInterfaceDriver(object):
# plugs interfaces using Linux Bridge
class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
def plug(self, network, mac_address):
def plug(self, network, mac_address, gateway=True):
if network.get('vlan', None) is not None:
LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
network['vlan'],
@ -874,7 +892,7 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
LinuxBridgeInterfaceDriver.ensure_bridge(
network['bridge'],
network['bridge_interface'],
network)
network, gateway)
return network['bridge']
@ -914,7 +932,7 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
@classmethod
@utils.synchronized('ensure_bridge', external=True)
def ensure_bridge(_self, bridge, interface, net_attrs=None):
def ensure_bridge(_self, bridge, interface, net_attrs=None, gateway=True):
"""Create a bridge unless it already exists.
:param interface: the interface to create the bridge on.
@ -974,19 +992,28 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
"can't enslave it to bridge %s.\n" % (interface, bridge)):
raise exception.Error('Failed to add interface: %s' % err)
iptables_manager.ipv4['filter'].add_rule('FORWARD',
# Don't forward traffic unless we were told to be a gateway
if gateway:
iptables_manager.ipv4['filter'].add_rule('FORWARD',
'--in-interface %s -j ACCEPT' % \
bridge)
iptables_manager.ipv4['filter'].add_rule('FORWARD',
iptables_manager.ipv4['filter'].add_rule('FORWARD',
'--out-interface %s -j ACCEPT' % \
bridge)
else:
iptables_manager.ipv4['filter'].add_rule('FORWARD',
'--in-interface %s -j DROP' % \
bridge)
iptables_manager.ipv4['filter'].add_rule('FORWARD',
'--out-interface %s -j DROP' % \
bridge)
# plugs interfaces using Open vSwitch
class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver):
def plug(self, network, mac_address):
dev = "gw-" + str(network['id'])
def plug(self, network, mac_address, gateway=True):
dev = "gw-" + str(network['uuid'][0:11])
if not _device_exists(dev):
bridge = FLAGS.linuxnet_ovs_integration_bridge
_execute('ovs-vsctl',
@ -1002,6 +1029,14 @@ class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver):
_execute('ip', 'link', 'set', dev, "address", mac_address,
run_as_root=True)
_execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
if not gateway:
# If we weren't instructed to act as a gateway then add the
# appropriate flows to block all non-dhcp traffic.
_execute('ovs-ofctl',
'add-flow', bridge, "priority=1,actions=drop")
_execute('ovs-ofctl', 'add-flow', bridge,
"udp,tp_dst=67,dl_dst=%s,priority=2,actions=normal" %
mac_address)
return dev
@ -1009,7 +1044,7 @@ class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver):
return self.get_dev(network)
def get_dev(self, network):
dev = "gw-" + str(network['id'])
dev = "gw-" + str(network['uuid'][0:11])
return dev
iptables_manager = IptablesManager()

View File

@ -95,6 +95,7 @@ flags.DEFINE_string('floating_range', '4.4.4.0/24',
'Floating IP address block')
flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
flags.DEFINE_string('gateway', None, 'Default IPv4 gateway')
flags.DEFINE_string('gateway_v6', None, 'Default IPv6 gateway')
flags.DEFINE_integer('cnt_vpn_clients', 0,
'Number of addresses reserved for vpn clients')
@ -491,6 +492,10 @@ class NetworkManager(manager.SchedulerDependentManager):
network_id,
host=host)
def get_dhcp_leases(self, ctxt, network_ref):
"""Broker the request to the driver to fetch the dhcp leases"""
return self.driver.get_dhcp_leases(ctxt, network_ref)
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
@ -863,7 +868,7 @@ class NetworkManager(manager.SchedulerDependentManager):
self._setup_network(context, network_ref)
def create_networks(self, context, label, cidr, multi_host, num_networks,
network_size, cidr_v6, gateway_v6, bridge,
network_size, cidr_v6, gateway, gateway_v6, bridge,
bridge_interface, dns1=None, dns2=None, **kwargs):
"""Create networks based on parameters."""
# NOTE(jkoelker): these are dummy values to make sure iter works
@ -947,7 +952,7 @@ class NetworkManager(manager.SchedulerDependentManager):
if cidr and subnet_v4:
net['cidr'] = str(subnet_v4)
net['netmask'] = str(subnet_v4.netmask)
net['gateway'] = str(subnet_v4[1])
net['gateway'] = gateway or str(subnet_v4[1])
net['broadcast'] = str(subnet_v4.broadcast)
net['dhcp_start'] = str(subnet_v4[2])

View File

@ -224,8 +224,6 @@ class Client(object):
type(data)))
def deserialize(self, data, status_code):
if status_code == 202:
return data
return JSONSerializer().deserialize(data, self.content_type())
def content_type(self, format=None):

View File

@ -15,6 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import time
from netaddr import IPNetwork, IPAddress
from nova import db
from nova import exception
from nova import flags
@ -33,6 +37,10 @@ flags.DEFINE_string('quantum_ipam_lib',
"Indicates underlying IP address management library")
flags.DEFINE_string('quantum_use_dhcp', 'False',
'Whether or not to enable DHCP for networks')
class QuantumManager(manager.FlatManager):
"""NetworkManager class that communicates with a Quantum service
via a web services API to provision VM network connectivity.
@ -43,7 +51,6 @@ class QuantumManager(manager.FlatManager):
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.
@ -65,9 +72,14 @@ class QuantumManager(manager.FlatManager):
self.ipam = utils.import_object(ipam_lib).get_ipam_lib(self)
super(QuantumManager, self).__init__(*args, **kwargs)
self.driver.init_host()
# TODO(bgh): We'll need to enable these when we implement the full L3
# functionalities
# self.driver.ensure_metadata_ip()
# self.driver.metadata_forward()
def create_networks(self, context, label, cidr, multi_host, num_networks,
network_size, cidr_v6, gateway_v6, bridge,
network_size, cidr_v6, gateway, gateway_v6, bridge,
bridge_interface, dns1=None, dns2=None, uuid=None,
**kwargs):
"""Unlike other NetworkManagers, with QuantumManager, each
@ -98,7 +110,10 @@ class QuantumManager(manager.FlatManager):
ipam_tenant_id = kwargs.get("project_id", None)
priority = kwargs.get("priority", 0)
self.ipam.create_subnet(context, label, ipam_tenant_id, quantum_net_id,
priority, cidr, gateway_v6, cidr_v6, dns1, dns2)
priority, cidr, gateway, gateway_v6,
cidr_v6, dns1, dns2)
return [{'uuid': quantum_net_id}]
def delete_network(self, context, fixed_range, uuid):
"""Lookup network by uuid, delete both the IPAM
@ -119,6 +134,9 @@ class QuantumManager(manager.FlatManager):
if self.q_conn.network_exists(p['id'], uuid):
project_id = p['id']
break
if project_id is None:
# If nothing was found we default to this
project_id = FLAGS.quantum_default_tenant_id
LOG.debug("Deleting network for tenant: %s" % project_id)
self.ipam.delete_subnets_by_net_id(context, quantum_net_id,
project_id)
@ -152,7 +170,7 @@ class QuantumManager(manager.FlatManager):
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)
LOG.debug(_("network allocations for instance %s"), project_id)
requested_networks = kwargs.get('requested_networks')
@ -163,9 +181,17 @@ class QuantumManager(manager.FlatManager):
net_proj_pairs = self.ipam.get_project_and_global_net_ids(context,
project_id)
# Quantum may also know about networks that aren't in the networks
# table so we need to query Quanutm for any tenant networks and add
# them to net_proj_pairs.
qnets = self.q_conn.get_networks(project_id)
for qn in qnets['networks']:
pair = (qn['id'], project_id)
if pair not in net_proj_pairs:
net_proj_pairs.append(pair)
# Create a port via quantum and attach the vif
for (quantum_net_id, project_id) in net_proj_pairs:
# 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
@ -176,8 +202,28 @@ class QuantumManager(manager.FlatManager):
# 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": project_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}
vif_rec = manager.FlatManager.add_virtual_interface(self,
context, instance_id, network_ref['id'])
@ -186,12 +232,72 @@ class QuantumManager(manager.FlatManager):
q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
self.q_conn.create_and_attach_port(q_tenant_id, quantum_net_id,
vif_rec['uuid'])
self.ipam.allocate_fixed_ip(context, project_id, quantum_net_id,
vif_rec)
# Tell melange to allocate an IP
ip = self.ipam.allocate_fixed_ip(context, project_id,
quantum_net_id, vif_rec)
# Set up/start the dhcp server for this network if necessary
if FLAGS.quantum_use_dhcp:
self.enable_dhcp(context, quantum_net_id, network_ref,
vif_rec, project_id)
return self.get_instance_nw_info(context, instance_id,
instance_type_id, host)
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
v4_subnet, v6_subnet = 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 [v4_subnet, v6_subnet]:
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 = IPNetwork(subnet['cidr'])
network_ref['dhcp_server'] = IPAddress(n.first + 1)
# TODO(bgh): Melange should probably track dhcp_start
if not 'dhcp_start' in network_ref or \
network_ref['dhcp_start'] is None:
network_ref['dhcp_start'] = IPAddress(n.first + 2)
network_ref['broadcast'] = IPAddress(n.broadcast)
network_ref['gateway'] = subnet['gateway']
# Construct the interface id that we'll use for the bridge
interface_id = "gw-" + str(network_ref['uuid'][0:11])
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
mac_address = self.generate_mac_address()
dev = self.driver.plug(network_ref, mac_address,
gateway=(network_ref['gateway'] != None))
self.driver.initialize_gateway_device(dev, 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)
else: # We've already got one and its plugged in
dev = interface_id
hosts = self.get_dhcp_hosts_text(context,
subnet['network_id'], project_id)
self.driver.update_dhcp_hostfile_with_text(dev, hosts)
self.driver.restart_dhcp(dev, network_ref)
def get_instance_nw_info(self, context, instance_id,
instance_type_id, host):
"""This method is used by compute to fetch all network data
@ -214,15 +320,9 @@ class QuantumManager(manager.FlatManager):
vifs = db.virtual_interface_get_by_instance(admin_context,
instance_id)
for vif in vifs:
q_tenant_id = project_id
ipam_tenant_id = project_id
net_id, port_id = self.q_conn.get_port_by_attachment(q_tenant_id,
vif['uuid'])
if not net_id:
q_tenant_id = FLAGS.quantum_default_tenant_id
ipam_tenant_id = None
net_id, port_id = self.q_conn.get_port_by_attachment(
q_tenant_id, vif['uuid'])
net = db.network_get(admin_context, vif['network_id'])
net_id = net['uuid']
if not net_id:
# TODO(bgh): We need to figure out a way to tell if we
# should actually be raising this exception or not.
@ -232,8 +332,13 @@ class QuantumManager(manager.FlatManager):
# probably just log, continue, and move on.
raise Exception(_("No network for for virtual interface %s") %
vif['uuid'])
(v4_subnet, v6_subnet) = self.ipam.get_subnets_by_net_id(context,
ipam_tenant_id, net_id)
ipam_tenant_id = self.ipam.get_tenant_id_by_net_id(context,
net_id, vif['uuid'], project_id)
v4_subnet, v6_subnet = \
self.ipam.get_subnets_by_net_id(context,
ipam_tenant_id, net_id, vif['uuid'])
v4_ips = self.ipam.get_v4_ips_by_interface(context,
net_id, vif['uuid'],
project_id=ipam_tenant_id)
@ -241,8 +346,6 @@ class QuantumManager(manager.FlatManager):
net_id, vif['uuid'],
project_id=ipam_tenant_id)
quantum_net_id = v4_subnet['network_id'] or v6_subnet['network_id']
def ip_dict(ip, subnet):
return {
"ip": ip,
@ -298,24 +401,35 @@ class QuantumManager(manager.FlatManager):
for vif_ref in vifs:
interface_id = vif_ref['uuid']
q_tenant_id = project_id
ipam_tenant_id = project_id
(net_id, port_id) = self.q_conn.get_port_by_attachment(q_tenant_id,
interface_id)
if not net_id:
network_ref = db.network_get(admin_context, vif_ref['network_id'])
net_id = network_ref['uuid']
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
ipam_tenant_id = None
(net_id, port_id) = self.q_conn.get_port_by_attachment(
q_tenant_id, interface_id)
if not net_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))
continue
self.q_conn.detach_and_delete_port(q_tenant_id,
net_id, port_id)
else:
self.q_conn.detach_and_delete_port(q_tenant_id,
net_id, port_id)
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)
# If DHCP is enabled on this network then we need to update the
# leases and restart the server.
if FLAGS.quantum_use_dhcp:
self.update_dhcp(context, ipam_tenant_id, network_ref, vif_ref,
project_id)
try:
db.virtual_interface_delete_by_instance(admin_context,
instance_id)
@ -323,6 +437,37 @@ class QuantumManager(manager.FlatManager):
LOG.error(_("Attempted to deallocate non-existent instance: %s" %
(instance_id)))
# TODO(bgh): At some point we should consider merging enable_dhcp() and
# update_dhcp()
def update_dhcp(self, context, ipam_tenant_id, network_ref, vif_ref,
project_id):
# Figure out what subnet corresponds to this network/vif
v4_subnet, v6_subnet = self.ipam.get_subnets_by_net_id(context,
ipam_tenant_id, network_ref['uuid'], vif_ref['uuid'])
for subnet in [v4_subnet, v6_subnet]:
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).
network_ref['cidr'] = subnet['cidr']
n = IPNetwork(subnet['cidr'])
network_ref['dhcp_server'] = IPAddress(n.first + 1)
network_ref['dhcp_start'] = IPAddress(n.first + 2)
network_ref['broadcast'] = IPAddress(n.broadcast)
network_ref['gateway'] = IPAddress(n.first + 1)
dev = "gw-" + str(network_ref['uuid'][0:11])
# And remove the dhcp mappings for the subnet
hosts = self.get_dhcp_hosts_text(context,
subnet['network_id'], project_id)
self.driver.update_dhcp_hostfile_with_text(dev, hosts)
# Restart dnsmasq
self.driver.kill_dhcp(dev)
self.driver.restart_dhcp(dev, network_ref)
# TODO(bgh): if this is the last instance for the network
# then we should actually just kill the dhcp server.
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
@ -334,6 +479,50 @@ class QuantumManager(manager.FlatManager):
project_id = context.project_id
for (net_id, _i) in networks:
self.ipam.verify_subnet_exists(context, project_id, net_id)
# 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)
if not self.q_conn.network_exists(project_id, net_id):
raise exception.NetworkNotFound(network_id=net_id)
# NOTE(bgh): deallocate_for_instance will take care of this.. The reason
# we're providing this is so that NetworkManager::release_fixed_ip() isn't
# called. It does some database operations that we don't want to happen
# and since the majority of the stuff that it does is already taken care
# of in our deallocate_for_instance call we don't need to do anything.
def release_fixed_ip(self, context, address):
pass
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

View File

@ -66,11 +66,12 @@ class MelangeConnection(object):
else:
return httplib.HTTPConnection(self.host, self.port)
def do_request(self, method, path, body=None, headers=None, params=None):
def do_request(self, method, path, body=None, headers=None, params=None,
content_type=".json"):
headers = headers or {}
params = params or {}
url = "/%s/%s.json" % (self.version, path)
url = "/%s/%s%s" % (self.version, path, content_type)
if params:
url += "?%s" % urllib.urlencode(params)
try:
@ -98,13 +99,14 @@ class MelangeConnection(object):
return json.loads(response)['ip_addresses']
def create_block(self, network_id, cidr,
project_id=None, dns1=None, dns2=None):
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', dns1=dns1, dns2=dns2))
type='private', gateway=gateway,
dns1=dns1, dns2=dns2))
self.post(url, body=json.dumps(req_params),
headers=json_content_type)
@ -132,6 +134,14 @@ class MelangeConnection(object):
response = self.get(url, headers=json_content_type)
return json.loads(response)['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 json.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 ""

View File

@ -15,8 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from netaddr import IPNetwork
from netaddr import IPNetwork, IPAddress
from nova import db
from nova import exception
from nova import flags
@ -45,7 +44,7 @@ class QuantumMelangeIPAMLib(object):
def create_subnet(self, context, label, project_id,
quantum_net_id, priority, cidr=None,
gateway_v6=None, cidr_v6=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.
@ -59,25 +58,34 @@ class QuantumMelangeIPAMLib(object):
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": project_id,
"priority": priority,
"label": label}
if FLAGS.quantum_use_dhcp:
if cidr:
n = IPNetwork(cidr)
net['dhcp_start'] = IPAddress(n.first + 2)
else:
net['dhcp_start'] = None
admin_context = context.elevated()
network = db.network_create_safe(admin_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'])
ip = self.m_conn.allocate_ip(quantum_net_id,
vif_ref['uuid'], project_id=tenant_id,
mac_address=vif_ref['address'])
return ip[0]['address']
def get_network_id_by_cidr(self, context, cidr, project_id):
"""Find the Quantum UUID associated with a IPv4 CIDR
@ -86,6 +94,7 @@ class QuantumMelangeIPAMLib(object):
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']:
LOG.debug("block: %s" % b)
if b['cidr'] == cidr:
return b['network_id']
raise exception.NotFound(_("No network found for cidr %s" % cidr))
@ -134,34 +143,43 @@ class QuantumMelangeIPAMLib(object):
return [(network_id, tenant_id)
for priority, network_id, tenant_id in priority_nets]
def get_subnets_by_net_id(self, context, project_id, net_id):
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]
for tid in tenant_ids:
try:
ips = self.m_conn.get_allocated_ips(net_id, vif_id, tid)
except Exception, e:
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.
"""
# FIXME(danwent): Melange actually returns the subnet info
# when we query for a particular interface. We may want to
# rework 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
all_blocks = self.m_conn.get_blocks(tenant_id)
for b in all_blocks['ip_blocks']:
if b['network_id'] == net_id:
subnet = {'network_id': b['network_id'],
'cidr': b['cidr'],
'gateway': b['gateway'],
'broadcast': b['broadcast'],
'netmask': b['netmask'],
'dns1': b['dns1'],
'dns2': b['dns2']}
ips = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id)
if IPNetwork(b['cidr']).version == 6:
subnet_v6 = subnet
else:
subnet_v4 = subnet
for ip_address in ips:
block = ip_address['ip_block']
print block
subnet = {'network_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_v4 = subnet
else:
subnet_v6 = subnet
return (subnet_v4, subnet_v6)
def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id):
@ -179,7 +197,7 @@ class QuantumMelangeIPAMLib(object):
project_id, 6)
def _get_ips_by_interface(self, context, net_id, vif_id, project_id,
ip_version):
ip_version):
"""Helper method to fetch v4 or v6 addresses for a particular
virtual interface.
"""
@ -192,10 +210,16 @@ class QuantumMelangeIPAMLib(object):
"""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
v4_subnet, v6_subnet = self.get_subnets_by_net_id(context, tenant_id,
quantum_net_id)
return v4_subnet is not None
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
@ -203,3 +227,7 @@ class QuantumMelangeIPAMLib(object):
"""
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]

View File

@ -51,7 +51,7 @@ class QuantumNovaIPAMLib(object):
def create_subnet(self, context, label, tenant_id,
quantum_net_id, priority, cidr=None,
gateway_v6=None, cidr_v6=None,
gateway=None, gateway_v6=None, cidr_v6=None,
dns1=None, dns2=None):
"""Re-use the basic FlatManager create_networks method to
initialize the networks and fixed_ips tables in Nova DB.
@ -63,7 +63,7 @@ class QuantumNovaIPAMLib(object):
subnet_size = len(netaddr.IPNetwork(cidr))
networks = manager.FlatManager.create_networks(self.net_manager,
admin_context, label, cidr,
False, 1, subnet_size, cidr_v6,
False, 1, subnet_size, cidr_v6, gateway,
gateway_v6, quantum_net_id, None, dns1, dns2)
if len(networks) != 1:
@ -117,6 +117,7 @@ class QuantumNovaIPAMLib(object):
"""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)
address = None
if network['cidr']:
address = db.fixed_ip_associate_pool(admin_context,
network['id'],
@ -124,8 +125,15 @@ class QuantumNovaIPAMLib(object):
values = {'allocated': True,
'virtual_interface_id': vif_rec['id']}
db.fixed_ip_update(admin_context, address, values)
return address
def get_subnets_by_net_id(self, context, tenant_id, net_id):
def get_tenant_id_by_net_id(self, context, net_id, vif_id, project_id):
"""Returns tenant_id for this network. This is only necessary
in the melange IPAM case.
"""
return project_id
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.
"""
@ -177,7 +185,8 @@ class QuantumNovaIPAMLib(object):
such subnet exists.
"""
admin_context = context.elevated()
db.network_get_by_uuid(admin_context, quantum_net_id)
net = db.network_get_by_uuid(admin_context, quantum_net_id)
return net is not None
def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref):
"""Deallocate all fixed IPs associated with the specified
@ -194,3 +203,20 @@ class QuantumNovaIPAMLib(object):
except exception.FixedIpNotFoundForInstance:
LOG.error(_('No fixed IPs to deallocate for vif %s' %
vif_ref['id']))
def get_allocated_ips(self, context, subnet_id, project_id):
"""Returns a list of (ip, vif_id) pairs"""
admin_context = context.elevated()
ips = db.fixed_ip_get_all(admin_context)
allocated_ips = []
# Get all allocated IPs that are part of this subnet
network = db.network_get_by_uuid(admin_context, subnet_id)
for ip in ips:
# Skip unallocated IPs
if not ip['allocated'] == 1:
continue
if ip['network_id'] == network['id']:
vif = db.virtual_interface_get(admin_context,
ip['virtual_interface_id'])
allocated_ips.append((ip['address'], vif['uuid']))
return allocated_ips

View File

@ -79,6 +79,10 @@ class QuantumClientConnection(object):
# 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):
"""Creates a Quantum port on the specified network, sets
status to ACTIVE to enable traffic, and attaches the
@ -102,21 +106,20 @@ class QuantumClientConnection(object):
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, attachment_id):
"""Given a tenant, search for the Quantum network and port
UUID that has the specified interface-id attachment.
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.
"""
# 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']
port_list_resdict = self.client.list_ports(net_id,
tenant=tenant_id)
for p in port_list_resdict["ports"]:
port_id = p["id"]
port_get_resdict = self.client.show_port_attachment(net_id,
port_list_resdict = self.client.list_ports(net_id, tenant=tenant_id)
for p in port_list_resdict["ports"]:
port_id = p["id"]
port_get_resdict = self.client.show_port_attachment(net_id,
port_id, tenant=tenant_id)
if attachment_id == port_get_resdict["attachment"]["id"]:
return (net_id, port_id)
return (None, None)
# Skip ports without an attachment
if "id" not in port_get_resdict["attachment"]:
continue
if attachment_id == port_get_resdict["attachment"]["id"]:
return port_id
return None

View File

@ -63,6 +63,7 @@ def setup():
num_networks=FLAGS.num_networks,
network_size=FLAGS.network_size,
cidr_v6=FLAGS.fixed_range_v6,
gateway=FLAGS.gateway,
gateway_v6=FLAGS.gateway_v6,
bridge=FLAGS.flat_network_bridge,
bridge_interface=bridge_interface,

View File

@ -206,7 +206,7 @@ class FlatNetworkTestCase(test.TestCase):
is_admin=True)
nets = self.network.create_networks(context_admin, 'fake',
'192.168.0.0/24', False, 1,
256, None, None, None, None)
256, None, None, None, None, None)
self.assertEqual(1, len(nets))
network = nets[0]
self.assertEqual(3, db.network_count_reserved_ips(context_admin,
@ -697,7 +697,7 @@ class CommonNetworkTestCase(test.TestCase):
manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/24',
False, 1, 256, None, None, None,
None)
None, None)
self.assertEqual(1, len(nets))
cidrs = [str(net['cidr']) for net in nets]
self.assertTrue('192.168.0.0/24' in cidrs)
@ -706,7 +706,7 @@ class CommonNetworkTestCase(test.TestCase):
manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/24',
False, 2, 128, None, None, None,
None)
None, None)
self.assertEqual(2, len(nets))
cidrs = [str(net['cidr']) for net in nets]
self.assertTrue('192.168.0.0/25' in cidrs)
@ -721,7 +721,7 @@ class CommonNetworkTestCase(test.TestCase):
self.mox.ReplayAll()
nets = manager.create_networks(None, 'fake', '192.168.0.0/16',
False, 4, 256, None, None, None,
None)
None, None)
self.assertEqual(4, len(nets))
cidrs = [str(net['cidr']) for net in nets]
exp_cidrs = ['192.168.0.0/24', '192.168.1.0/24', '192.168.3.0/24',
@ -740,7 +740,7 @@ class CommonNetworkTestCase(test.TestCase):
# ValueError: requested cidr (192.168.2.0/24) conflicts with
# existing smaller cidr
args = (None, 'fake', '192.168.2.0/24', False, 1, 256, None, None,
None, None)
None, None, None)
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_split_smaller_cidr_in_use(self):
@ -751,7 +751,8 @@ class CommonNetworkTestCase(test.TestCase):
'cidr': '192.168.2.0/25'}])
self.mox.ReplayAll()
nets = manager.create_networks(None, 'fake', '192.168.0.0/16',
False, 4, 256, None, None, None, None)
False, 4, 256, None, None, None, None,
None)
self.assertEqual(4, len(nets))
cidrs = [str(net['cidr']) for net in nets]
exp_cidrs = ['192.168.0.0/24', '192.168.1.0/24', '192.168.3.0/24',
@ -768,7 +769,8 @@ class CommonNetworkTestCase(test.TestCase):
'cidr': '192.168.2.9/29'}])
self.mox.ReplayAll()
nets = manager.create_networks(None, 'fake', '192.168.2.0/24',
False, 3, 32, None, None, None, None)
False, 3, 32, None, None, None, None,
None)
self.assertEqual(3, len(nets))
cidrs = [str(net['cidr']) for net in nets]
exp_cidrs = ['192.168.2.32/27', '192.168.2.64/27', '192.168.2.96/27']
@ -786,7 +788,7 @@ class CommonNetworkTestCase(test.TestCase):
manager.db.network_get_all(ctxt).AndReturn(in_use)
self.mox.ReplayAll()
args = (None, 'fake', '192.168.2.0/24', False, 3, 64, None, None,
None, None)
None, None, None)
# ValueError: Not enough subnets avail to satisfy requested num_
# networks - some subnets in requested range already
# in use
@ -795,7 +797,7 @@ class CommonNetworkTestCase(test.TestCase):
def test_validate_cidrs_one_in_use(self):
manager = fake_network.FakeNetworkManager()
args = (None, 'fake', '192.168.0.0/24', False, 2, 256, None, None,
None, None)
None, None, None)
# ValueError: network_size * num_networks exceeds cidr size
self.assertRaises(ValueError, manager.create_networks, *args)
@ -808,13 +810,13 @@ class CommonNetworkTestCase(test.TestCase):
self.mox.ReplayAll()
# ValueError: cidr already in use
args = (None, 'fake', '192.168.0.0/24', False, 1, 256, None, None,
None, None)
None, None, None)
self.assertRaises(ValueError, manager.create_networks, *args)
def test_validate_cidrs_too_many(self):
manager = fake_network.FakeNetworkManager()
args = (None, 'fake', '192.168.0.0/24', False, 200, 256, None, None,
None, None)
None, None, None)
# ValueError: Not enough subnets avail to satisfy requested
# num_networks
self.assertRaises(ValueError, manager.create_networks, *args)
@ -822,7 +824,8 @@ class CommonNetworkTestCase(test.TestCase):
def test_validate_cidrs_split_partial(self):
manager = fake_network.FakeNetworkManager()
nets = manager.create_networks(None, 'fake', '192.168.0.0/16',
False, 2, 256, None, None, None, None)
False, 2, 256, None, None, None, None,
None)
returned_cidrs = [str(net['cidr']) for net in nets]
self.assertTrue('192.168.0.0/24' in returned_cidrs)
self.assertTrue('192.168.1.0/24' in returned_cidrs)
@ -835,7 +838,7 @@ class CommonNetworkTestCase(test.TestCase):
manager.db.network_get_all(ctxt).AndReturn(fakecidr)
self.mox.ReplayAll()
args = (None, 'fake', '192.168.0.0/24', False, 1, 256, None, None,
None, None)
None, None, None)
# ValueError: requested cidr (192.168.0.0/24) conflicts
# with existing supernet
self.assertRaises(ValueError, manager.create_networks, *args)
@ -846,7 +849,7 @@ class CommonNetworkTestCase(test.TestCase):
self.stubs.Set(manager, '_create_fixed_ips',
self.fake_create_fixed_ips)
args = [None, 'foo', cidr, None, 1, 256, 'fd00::/48', None, None,
None]
None, None, None]
self.assertTrue(manager.create_networks(*args))
def test_create_networks_cidr_already_used(self):
@ -857,7 +860,7 @@ class CommonNetworkTestCase(test.TestCase):
manager.db.network_get_all(ctxt).AndReturn(fakecidr)
self.mox.ReplayAll()
args = [None, 'foo', '192.168.0.0/24', None, 1, 256,
'fd00::/48', None, None, None]
'fd00::/48', None, None, None, None, None]
self.assertRaises(ValueError, manager.create_networks, *args)
def test_create_networks_many(self):
@ -866,7 +869,7 @@ class CommonNetworkTestCase(test.TestCase):
self.stubs.Set(manager, '_create_fixed_ips',
self.fake_create_fixed_ips)
args = [None, 'foo', cidr, None, 10, 256, 'fd00::/48', None, None,
None]
None, None, None]
self.assertTrue(manager.create_networks(*args))
def test_get_instance_uuids_by_ip_regex(self):

View File

@ -108,11 +108,17 @@ class NetworkCommandsTestCase(test.TestCase):
self.assertEqual(cidr, self.fake_net['cidr'])
return db_fakes.FakeModel(self.fake_net)
def fake_network_get_by_uuid(context, uuid):
self.assertTrue(context.to_dict()['is_admin'])
self.assertEqual(uuid, self.fake_net['uuid'])
return db_fakes.FakeModel(self.fake_net)
def fake_network_update(context, network_id, values):
self.assertTrue(context.to_dict()['is_admin'])
self.assertEqual(network_id, self.fake_net['id'])
self.assertEqual(values, self.fake_update_value)
self.fake_network_get_by_cidr = fake_network_get_by_cidr
self.fake_network_get_by_uuid = fake_network_get_by_uuid
self.fake_network_update = fake_network_update
def tearDown(self):
@ -131,6 +137,7 @@ class NetworkCommandsTestCase(test.TestCase):
self.assertEqual(kwargs['vlan_start'], 200)
self.assertEqual(kwargs['vpn_start'], 2000)
self.assertEqual(kwargs['cidr_v6'], 'fd00:2::/120')
self.assertEqual(kwargs['gateway'], '10.2.0.1')
self.assertEqual(kwargs['gateway_v6'], 'fd00:2::22')
self.assertEqual(kwargs['bridge'], 'br200')
self.assertEqual(kwargs['bridge_interface'], 'eth0')
@ -149,11 +156,13 @@ class NetworkCommandsTestCase(test.TestCase):
vlan_start=200,
vpn_start=2000,
fixed_range_v6='fd00:2::/120',
gateway='10.2.0.1',
gateway_v6='fd00:2::22',
bridge='br200',
bridge_interface='eth0',
dns1='8.8.8.8',
dns2='8.8.4.4')
dns2='8.8.4.4',
uuid='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
def test_list(self):
@ -193,14 +202,14 @@ class NetworkCommandsTestCase(test.TestCase):
self.fake_net = self.net
self.fake_net['project_id'] = None
self.fake_net['host'] = None
self.stubs.Set(db, 'network_get_by_cidr',
self.fake_network_get_by_cidr)
self.stubs.Set(db, 'network_get_by_uuid',
self.fake_network_get_by_uuid)
def fake_network_delete_safe(context, network_id):
self.assertTrue(context.to_dict()['is_admin'])
self.assertEqual(network_id, self.fake_net['id'])
self.stubs.Set(db, 'network_delete_safe', fake_network_delete_safe)
self.commands.delete(fixed_range=self.fake_net['cidr'])
self.commands.delete(uuid=self.fake_net['uuid'])
def test_delete_by_cidr(self):
self.fake_net = self.net

View File

@ -26,6 +26,8 @@ from nova.network.quantum import manager as quantum_manager
from nova import test
from nova import utils
import mox
LOG = logging.getLogger('nova.tests.quantum_network')
@ -41,7 +43,7 @@ class FakeQuantumClientConnection(object):
for net_id, n in self.nets.items():
if n['tenant-id'] == tenant_id:
net_ids.append(net_id)
return net_ids
return {'networks': net_ids}
def create_network(self, tenant_id, network_name):
@ -90,14 +92,22 @@ class FakeQuantumClientConnection(object):
"for tenant %(tenant_id)s" % locals()))
del self.nets[net_id]['ports'][port_id]
def get_port_by_attachment(self, tenant_id, attachment_id):
for net_id, n in self.nets.items():
if n['tenant-id'] == tenant_id:
def get_port_by_attachment(self, tenant_id, net_id, attachment_id):
for nid, n in self.nets.items():
if nid == net_id and n['tenant-id'] == tenant_id:
for port_id, p in n['ports'].items():
if p['attachment-id'] == attachment_id:
return (net_id, port_id)
return port_id
return None
def get_networks(self, tenant_id):
nets = []
for nid, n in self.nets.items():
if n['tenant-id'] == tenant_id:
x = {'id': nid}
nets.append(x)
return {'networks': nets}
return (None, None)
networks = [{'label': 'project1-net1',
'injected': False,
@ -184,14 +194,16 @@ class QuantumTestCaseBase(object):
def _create_nets(self):
for n in networks:
ctx = context.RequestContext('user1', n['project_id'])
self.net_man.create_networks(ctx,
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'],
dns2=n['dns2'], project_id=n['project_id'],
priority=n['priority'])
n['uuid'] = nwks[0]['uuid']
def _delete_nets(self):
for n in networks:
@ -210,6 +222,16 @@ class QuantumTestCaseBase(object):
instance_ref = db.instance_create(ctx,
{"project_id": project_id})
def func(arg1, arg2):
pass
def func1(arg1):
pass
self.net_man.driver.update_dhcp_hostfile_with_text = func
self.net_man.driver.restart_dhcp = func
self.net_man.driver.kill_dhcp = func1
nw_info = self.net_man.allocate_for_instance(ctx,
instance_id=instance_ref['id'], host="",
instance_type_id=instance_ref['instance_type_id'],
@ -249,12 +271,23 @@ class QuantumTestCaseBase(object):
ctx = context.RequestContext('user1', project_id)
net_ids = self.net_man.q_conn.get_networks_for_tenant(project_id)
requested_networks = [(net_id, None) for net_id in net_ids]
requested_networks = [(net_id, None) for net_id in
net_ids['networks']]
self.net_man.validate_networks(ctx, requested_networks)
instance_ref = db.instance_create(ctx,
{"project_id": project_id})
def func(arg1, arg2):
pass
def func1(arg1):
pass
self.net_man.driver.update_dhcp_hostfile_with_text = func
self.net_man.driver.restart_dhcp = func
self.net_man.driver.kill_dhcp = func1
nw_info = self.net_man.allocate_for_instance(ctx,
instance_id=instance_ref['id'], host="",
instance_type_id=instance_ref['instance_type_id'],