1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 Nicira Networks, Inc
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
22
from nova import context
24
from nova import exception
25
from nova import flags
26
from nova.network import manager
27
from nova.network.quantum import melange_ipam_lib
28
from nova.network.quantum import quantum_connection
29
from nova.openstack.common import cfg
30
from nova.openstack.common import log as logging
31
from nova.openstack.common import rpc
32
from nova import utils
34
LOG = logging.getLogger(__name__)
37
cfg.StrOpt('quantum_ipam_lib',
38
default='nova.network.quantum.nova_ipam_lib',
39
help="Indicates underlying IP address management library"),
40
# TODO(Vek): Eventually, this needs to mean more than just using
41
# Melange for assignment of MAC addresses (with an
42
# appropriate flag name change, of course), but this is all
44
cfg.BoolOpt('use_melange_mac_generation',
46
help="Use Melange for assignment of MAC addresses"),
47
cfg.BoolOpt('quantum_use_dhcp',
49
help='Whether or not to enable DHCP for networks'),
50
cfg.BoolOpt('quantum_use_port_security',
52
help='Whether or not to enable port security'),
53
cfg.BoolOpt('quantum_port_security_include_link_local',
55
help='Add the link local address to the port security list'),
59
FLAGS.register_opts(quantum_opts)
62
class QuantumManager(manager.FloatingIP, manager.FlatManager):
63
"""NetworkManager class that communicates with a Quantum service
64
via a web services API to provision VM network connectivity.
66
For IP Address management, QuantumManager can be configured to
67
use either Nova's local DB or the Melange IPAM service.
70
DHCP = FLAGS.quantum_use_dhcp
72
def __init__(self, q_conn=None, ipam_lib=None, *args, **kwargs):
73
"""Initialize two key libraries, the connection to a
74
Quantum service, and the library for implementing IPAM.
76
Calls inherited FlatManager constructor.
80
q_conn = quantum_connection.QuantumClientConnection()
84
ipam_lib = FLAGS.quantum_ipam_lib
85
self._import_ipam_lib(ipam_lib)
87
super(QuantumManager, self).__init__(*args, **kwargs)
90
# Initialize general L3 networking
91
self.l3driver.initialize()
92
super(QuantumManager, self).init_host()
93
# Initialize floating ip support (only works for nova ipam currently)
94
if FLAGS.quantum_ipam_lib == 'nova.network.quantum.nova_ipam_lib':
95
LOG.debug("Initializing FloatingIP support")
96
self.init_host_floating_ips()
97
# Set up all the forwarding rules for any network that has a
99
networks = self.get_all_networks(context.get_admin_context())
102
# Don't update host information for network that does not
104
if net['host'] != self.host:
107
LOG.debug("Initializing NAT: %s (cidr: %s, gw: %s)" % (
108
net['label'], net['cidr'], net['gateway']))
109
cidrs.append(net['cidr'])
110
self._update_network_host(context.get_admin_context(),
112
# .. and for each network
114
self.l3driver.initialize_network(c)
116
# Similar to FlatDHCPMananger, except we check for quantum_use_dhcp flag
117
# before we try to update_dhcp
118
def _setup_network_on_host(self, context, network):
119
"""Sets up network on this host."""
120
network['dhcp_server'] = self._get_dhcp_ip(context, network)
121
self.l3driver.initialize_gateway(network)
123
if FLAGS.quantum_use_dhcp and not FLAGS.fake_network:
124
dev = self.driver.get_dev(network)
125
self.driver.update_dhcp(context, dev, network)
127
self.driver.update_ra(context, dev, network)
128
gateway = utils.get_my_linklocal(dev)
129
self.db.network_update(context, network['id'],
130
{'gateway_v6': gateway})
132
def _update_network_host(self, context, net_uuid):
133
"""Set the host column in the networks table: note that this won't
134
work with multi-host but QuantumManager doesn't support that
135
anyways. The floating IPs mixin required network['host'] to be
137
entry = db.network_get_by_uuid(context.elevated(), net_uuid)
138
entry['host'] = self.host
139
db.network_update(context.elevated(), entry['id'], entry)
141
def _get_nova_id(self, instance=None):
142
# When creating the network we need to pass in an identifier for
143
# this zone. Some Quantum plugins need this information in order
144
# to set up appropriate networking.
145
if instance and instance['availability_zone']:
146
return instance['availability_zone']
148
return FLAGS.node_availability_zone
150
def get_all_networks(self, context):
152
networks.extend(self.ipam.get_global_networks(context))
153
networks.extend(self.ipam.get_project_networks(context))
156
def create_networks(self, context, label, cidr, multi_host, num_networks,
157
network_size, cidr_v6, gateway, gateway_v6, bridge,
158
bridge_interface, dns1=None, dns2=None, uuid=None,
160
"""Unlike other NetworkManagers, with QuantumManager, each
161
create_networks calls should create only a single network.
164
- no 'uuid' is specified, in which case we contact
165
Quantum and create a new network.
166
- an existing 'uuid' is specified, corresponding to
167
a Quantum network created out of band.
169
In both cases, we initialize a subnet using the IPAM lib.
171
# Enforce Configuration sanity.
173
# These flags are passed in from bin/nova-manage. The script
174
# collects the arguments and then passes them in through this
175
# function call. Note that in some cases, the script pre-processes
176
# the arguments, and sets them to a default value if a parameter's
177
# value was not specified on the command line. For pre-processed
178
# parameters, the most effective check to see if the user passed it
179
# in is to see if is different from the default value. (This
180
# does miss the use case where the user passes in the default value
181
# on the command line -- but it is unavoidable.)
182
if multi_host != FLAGS.multi_host:
183
# User specified it on the command line.
184
raise Exception(_("QuantumManager does not use 'multi_host'"
187
if num_networks != 1:
188
raise Exception(_("QuantumManager requires that only one"
189
" network is created per call"))
191
if network_size != int(FLAGS.network_size):
192
# User specified it on the command line.
193
LOG.warning("Ignoring unnecessary parameter 'network_size'")
195
if kwargs.get('vlan_start', None):
196
if kwargs['vlan_start'] != int(FLAGS.vlan_start):
197
# User specified it on the command line.
198
LOG.warning(_("QuantumManager does not use 'vlan_start'"
201
if kwargs.get('vpn_start', None):
202
if kwargs['vpn_start'] != int(FLAGS.vpn_start):
203
# User specified it on the command line.
204
LOG.warning(_("QuantumManager does not use 'vpn_start'"
207
if bridge is not None and len(bridge) > 0:
208
LOG.warning(_("QuantumManager does not use 'bridge'"
211
if bridge_interface is not None and len(bridge_interface) > 0:
212
LOG.warning(_("QuantumManager does not use 'bridge_interface'"
215
if gateway is not None and len(gateway) > 0:
216
if gateway.split('.')[3] != '1':
217
raise Exception(_("QuantumManager requires a valid (.1)"
218
" gateway address."))
220
q_tenant_id = kwargs["project_id"] or FLAGS.quantum_default_tenant_id
221
quantum_net_id = uuid
222
# If a uuid was specified with the network it should have already been
223
# created in Quantum, so make sure.
225
if not self.q_conn.network_exists(q_tenant_id, quantum_net_id):
226
raise Exception(_("Unable to find existing quantum "
227
"network for tenant '%(q_tenant_id)s' "
228
"with net-id '%(quantum_net_id)s'") %
231
nova_id = self._get_nova_id()
232
quantum_net_id = self.q_conn.create_network(q_tenant_id, label,
235
ipam_tenant_id = kwargs.get("project_id", None)
236
priority = kwargs.get("priority", 0)
237
# NOTE(tr3buchet): this call creates a nova network in the nova db
238
self.ipam.create_subnet(context, label, ipam_tenant_id,
239
quantum_net_id, priority, cidr,
240
gateway, gateway_v6, cidr_v6, dns1, dns2)
242
self._update_network_host(context, quantum_net_id)
244
# Initialize forwarding
245
self.l3driver.initialize_network(cidr)
247
return [{'uuid': quantum_net_id}]
249
def delete_network(self, context, fixed_range, uuid):
250
"""Lookup network by uuid, delete both the IPAM
251
subnet and the corresponding Quantum network.
253
The fixed_range parameter is kept here for interface compatibility
256
net_ref = db.network_get_by_uuid(context.elevated(), uuid)
257
project_id = net_ref['project_id']
258
q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
259
net_uuid = net_ref['uuid']
261
# Check for any attached ports on the network and fail the deletion if
262
# there is anything but the gateway port attached. If it is only the
263
# gateway port, unattach and delete it.
264
ports = self.q_conn.get_attached_ports(q_tenant_id, net_uuid)
265
num_ports = len(ports)
266
gw_interface_id = self.driver.get_dev(net_ref)
268
if gw_interface_id is not None:
269
gw_port_uuid = self.q_conn.get_port_by_attachment(q_tenant_id,
270
net_uuid, gw_interface_id)
276
raise exception.NetworkBusy(network=net_uuid)
278
# only delete gw ports if we are going to finish deleting network
280
self.q_conn.detach_and_delete_port(q_tenant_id,
283
self.l3driver.remove_gateway(net_ref)
285
# Now we can delete the network
286
self.q_conn.delete_network(q_tenant_id, net_uuid)
287
LOG.debug("Deleting network %s for tenant: %s" %
288
(net_uuid, q_tenant_id))
289
self.ipam.delete_subnets_by_net_id(context, net_uuid, project_id)
291
if FLAGS.quantum_use_dhcp:
292
if net_ref['host'] == self.host:
293
self.kill_dhcp(net_ref)
295
topic = rpc.queue_get_for(context,
299
rpc.call(context, topic, {'method': 'kill_dhcp',
300
'args': {'net_ref': net_ref}})
302
def kill_dhcp(self, net_ref):
303
dev = self.driver.get_dev(net_ref)
304
if self.driver._device_exists(dev):
305
self.driver.kill_dhcp(dev)
307
def allocate_for_instance(self, context, **kwargs):
308
"""Called by compute when it is creating a new VM.
310
There are three key tasks:
311
- Determine the number and order of vNICs to create
312
- Allocate IP addresses
313
- Create ports on a Quantum network and attach vNICs.
315
We support two approaches to determining vNICs:
316
- By default, a VM gets a vNIC for any network belonging
317
to the VM's project, and a vNIC for any "global" network
318
that has a NULL project_id. vNIC order is determined
319
by the network's 'priority' field.
320
- If the 'os-create-server-ext' was used to create the VM,
321
only the networks in 'requested_networks' are used to
322
create vNICs, and the vNIC order is determiend by the
323
order in the requested_networks array.
325
For each vNIC, use the FlatManager to create the entries
326
in the virtual_interfaces table, contact Quantum to
327
create a port and attachment the vNIC, and use the IPAM
328
lib to allocate IP addresses.
330
instance_id = kwargs['instance_id']
331
rxtx_factor = kwargs['rxtx_factor']
332
host = kwargs['host']
333
project_id = kwargs['project_id']
334
LOG.debug(_("network allocations for instance %s"), project_id)
335
requested_networks = kwargs.get('requested_networks')
336
instance = db.instance_get(context, instance_id)
338
net_proj_pairs = self.ipam.get_project_and_global_net_ids(context,
340
if requested_networks:
341
# need to figure out if a requested network is owned
342
# by the tenant, or by the provider
343
# Note: these are the only possible options, as the compute
344
# API already validated networks using validate_network()
345
proj_net_ids = set([p[0] for p in net_proj_pairs if p[1]])
347
for net_id, _i in requested_networks:
348
if net_id in proj_net_ids:
349
net_proj_pairs.append((net_id, project_id))
351
net_proj_pairs.append((net_id, None))
353
# Create a port via quantum and attach the vif
354
for proj_pair in net_proj_pairs:
355
network = self.get_network(context, proj_pair)
357
# TODO(tr3buchet): broken. Virtual interfaces require an integer
358
# network ID and it is not nullable
359
vif_rec = self.add_virtual_interface(context,
364
# talk to Quantum API to create and attach port.
365
nova_id = self._get_nova_id(instance)
366
# Tell the ipam library to allocate an IP
367
ips = self.ipam.allocate_fixed_ips(context, project_id,
368
network['quantum_net_id'], network['net_tenant_id'],
371
# Set up port security if enabled
372
if FLAGS.quantum_use_port_security:
373
if FLAGS.quantum_port_security_include_link_local:
374
mac = netaddr.EUI(vif_rec['address'])
375
ips.append(str(mac.ipv6_link_local()))
377
pairs = [{'mac_address': vif_rec['address'],
378
'ip_address': ip} for ip in ips]
380
self.q_conn.create_and_attach_port(network['net_tenant_id'],
381
network['quantum_net_id'],
383
vm_id=instance['uuid'],
384
rxtx_factor=rxtx_factor,
386
allowed_address_pairs=pairs)
387
# Set up/start the dhcp server for this network if necessary
388
if FLAGS.quantum_use_dhcp:
389
if network['host'] == self.host:
390
self.enable_dhcp(context, network['quantum_net_id'],
391
network, vif_rec, network['net_tenant_id'])
393
topic = rpc.queue_get_for(context,
394
FLAGS.network_topic, network['host'])
395
rpc.call(context, topic, {'method': 'enable_dhcp',
396
'args': {'quantum_net_id': network['quantum_net_id'],
397
'network_ref': network,
399
'project_id': network['net_tenant_id']}})
401
return self.get_instance_nw_info(context, instance_id,
404
project_id=project_id)
406
def get_network(self, context, proj_pair):
407
(quantum_net_id, net_tenant_id) = proj_pair
409
net_tenant_id = net_tenant_id or FLAGS.quantum_default_tenant_id
410
# FIXME(danwent): We'd like to have the manager be
411
# completely decoupled from the nova networks table.
412
# However, other parts of nova sometimes go behind our
413
# back and access network data directly from the DB. So
414
# for now, the quantum manager knows that there is a nova
415
# networks DB table and accesses it here. updating the
416
# virtual_interfaces table to use UUIDs would be one
417
# solution, but this would require significant work
419
admin_context = context.elevated()
421
# We may not be able to get a network_ref here if this network
422
# isn't in the database (i.e. it came from Quantum).
423
network_ref = db.network_get_by_uuid(admin_context,
426
if network_ref is None:
428
network_ref = {"uuid": quantum_net_id,
429
"project_id": net_tenant_id,
430
# NOTE(bgh): We need to document this somewhere but since
431
# we don't know the priority of any networks we get from
432
# quantum we just give them a priority of 0. If its
433
# necessary to specify the order of the vifs and what
434
# network they map to then the user will have to use the
435
# OSCreateServer extension and specify them explicitly.
437
# In the future users will be able to tag quantum networks
438
# with a priority .. and at that point we can update the
439
# code here to reflect that.
442
"label": "quantum-net-%s" % quantum_net_id}
443
network_ref['net_tenant_id'] = net_tenant_id
444
network_ref['quantum_net_id'] = quantum_net_id
447
@manager.wrap_check_policy
448
def get_instance_uuids_by_ip_filter(self, context, filters):
449
# This is not returning the instance IDs like the method name
450
# would make you think; it is matching the return format of
451
# the method it's overriding.
452
instance_ids = self.ipam.get_instance_ids_by_ip_address(
453
context, filters.get('ip'))
454
instances = [db.instance_get(context, id) for id in instance_ids]
455
return [{'instance_uuid':instance.uuid} for instance in instances]
457
@utils.synchronized('quantum-enable-dhcp')
458
def enable_dhcp(self, context, quantum_net_id, network_ref, vif_rec,
460
LOG.info("Using DHCP for network: %s" % network_ref['label'])
461
# Figure out the ipam tenant id for this subnet: We need to
462
# query for the tenant_id since the network could be created
463
# with the project_id as the tenant or the default tenant.
464
ipam_tenant_id = self.ipam.get_tenant_id_by_net_id(context,
465
quantum_net_id, vif_rec['uuid'], project_id)
466
# Figure out what subnets correspond to this network
467
subnets = self.ipam.get_subnets_by_net_id(context,
468
ipam_tenant_id, quantum_net_id, vif_rec['uuid'])
470
# Set up (or find) the dhcp server for each of the subnets
471
# returned above (both v4 and v6).
472
for subnet in subnets:
473
if subnet is None or subnet['cidr'] is None:
475
# Fill in some of the network fields that we would have
476
# previously gotten from the network table (they'll be
477
# passed to the linux_net functions).
478
network_ref['cidr'] = subnet['cidr']
479
n = netaddr.IPNetwork(subnet['cidr'])
480
# NOTE(tr3buchet): should probably not always assume first+1
481
network_ref['dhcp_server'] = netaddr.IPAddress(n.first + 1)
482
# TODO(bgh): Melange should probably track dhcp_start
483
# TODO(tr3buchet): melange should store dhcp_server as well
484
if (not 'dhcp_start' in network_ref or
485
network_ref['dhcp_start'] is None):
486
network_ref['dhcp_start'] = netaddr.IPAddress(n.first + 2)
487
network_ref['broadcast'] = netaddr.IPAddress(n.broadcast)
488
network_ref['gateway'] = subnet['gateway']
489
# Construct the interface id that we'll use for the bridge
490
interface_id = self.driver.get_dev(network_ref)
491
network_ref['bridge'] = interface_id
492
# Query quantum to see if we've already created a port for
493
# the gateway device and attached the device to the port.
494
# If we haven't then we need to intiialize it and create
495
# it. This device will be the one serving dhcp via
497
q_tenant_id = project_id or FLAGS.quantum_default_tenant_id
498
port = self.q_conn.get_port_by_attachment(q_tenant_id,
499
quantum_net_id, interface_id)
501
if not port: # No dhcp server has been started
502
self.l3driver.initialize_gateway(network_ref)
503
LOG.debug("Intializing DHCP for network: %s" %
505
self.q_conn.create_and_attach_port(q_tenant_id,
506
quantum_net_id, interface_id)
508
hosts = self.get_dhcp_hosts_text(context,
509
subnet['network_id'], project_id)
510
self.driver.update_dhcp_hostfile_with_text(interface_id, hosts)
511
self.driver.restart_dhcp(context, interface_id, network_ref)
513
def add_virtual_interface(self, context, instance_id, network_id,
515
# If we're not using melange, use the default means...
516
if FLAGS.use_melange_mac_generation:
517
return self._add_virtual_interface(context, instance_id,
518
network_id, net_tenant_id)
520
return super(QuantumManager, self).add_virtual_interface(context,
524
def _add_virtual_interface(self, context, instance_id, network_id,
526
vif = {'instance_id': instance_id,
527
'network_id': network_id,
528
'uuid': str(utils.gen_uuid())}
530
# TODO(Vek): Ideally, we would have a VirtualInterface class
531
# that would take care of delegating to whoever it
532
# needs to get information from. We'll look at
533
# this after Trey's refactorings...
534
m_ipam = melange_ipam_lib.get_ipam_lib(self)
535
vif['address'] = m_ipam.create_vif(vif['uuid'],
539
return self.db.virtual_interface_create(context, vif)
541
def get_instance_nw_info(self, context, instance_id, instance_uuid,
542
rxtx_factor, host, **kwargs):
543
"""This method is used by compute to fetch all network data
544
that should be used when creating the VM.
546
The method simply loops through all virtual interfaces
547
stored in the nova DB and queries the IPAM lib to get
548
the associated IP data.
550
The format of returned data is 'defined' by the initial
551
set of NetworkManagers found in nova/network/manager.py .
552
Ideally this 'interface' will be more formally defined
555
project_id = kwargs['project_id']
556
vifs = db.virtual_interface_get_by_instance(context, instance_id)
558
net_tenant_dict = dict((net_id, tenant_id)
559
for (net_id, tenant_id)
560
in self.ipam.get_project_and_global_net_ids(
561
context, project_id))
564
if vif.get('network_id') is not None:
565
network = db.network_get(context.elevated(), vif['network_id'])
566
net_tenant_id = net_tenant_dict[network['uuid']]
567
if net_tenant_id is None:
568
net_tenant_id = FLAGS.quantum_default_tenant_id
569
network = {'id': network['id'],
570
'uuid': network['uuid'],
571
'bridge': '', # Quantum ignores this field
572
'label': network['label'],
573
'injected': FLAGS.flat_injected,
574
'project_id': net_tenant_id}
575
networks[vif['uuid']] = network
577
# update instance network cache and return network_info
578
nw_info = self.build_network_info_model(context, vifs, networks,
580
db.instance_info_cache_update(context, instance_uuid,
581
{'network_info': nw_info.json()})
585
def deallocate_for_instance(self, context, **kwargs):
586
"""Called when a VM is terminated. Loop through each virtual
587
interface in the Nova DB and remove the Quantum port and
588
clear the IP allocation using the IPAM. Finally, remove
589
the virtual interfaces from the Nova DB.
591
instance_id = kwargs.get('instance_id')
592
project_id = kwargs.pop('project_id', None)
594
admin_context = context.elevated()
595
vifs = db.virtual_interface_get_by_instance(admin_context,
599
network = db.network_get(admin_context, vif['network_id'])
601
self.deallocate_port(vif['uuid'], network['uuid'], project_id,
604
ipam_tenant_id = self.deallocate_ip_address(context,
605
network['uuid'], project_id, vif, instance_id)
607
if FLAGS.quantum_use_dhcp:
608
if network['host'] == self.host:
609
self.update_dhcp(context, ipam_tenant_id, network,
612
topic = rpc.queue_get_for(context,
613
FLAGS.network_topic, network['host'])
614
rpc.call(context, topic, {'method': 'update_dhcp',
615
'args': {'ipam_tenant_id': ipam_tenant_id,
616
'network_ref': network,
618
'project_id': network['project_id']}})
620
db.virtual_interface_delete(admin_context, vif['id'])
622
def deallocate_port(self, interface_id, net_id, q_tenant_id, instance_id):
625
port_id = self.q_conn.get_port_by_attachment(q_tenant_id,
626
net_id, interface_id)
628
q_tenant_id = FLAGS.quantum_default_tenant_id
629
port_id = self.q_conn.get_port_by_attachment(
630
q_tenant_id, net_id, interface_id)
633
LOG.error("Unable to find port with attachment: %s" %
636
self.q_conn.detach_and_delete_port(q_tenant_id,
639
# except anything so the rest of deallocate can succeed
640
LOG.exception(_('port deallocation failed for instance: '
641
'|%(instance_id)s|, port_id: |%(port_id)s|') % locals())
643
def deallocate_ip_address(self, context, net_id,
644
project_id, vif_ref, instance_id):
645
ipam_tenant_id = None
647
ipam_tenant_id = self.ipam.get_tenant_id_by_net_id(context,
652
self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id,
656
# except anything so the rest of deallocate can succeed
657
vif_uuid = vif_ref['uuid']
658
msg = _('ipam deallocation failed for instance: '
659
'|%(instance_id)s|, vif_uuid: |%(vif_uuid)s|') % locals()
661
return ipam_tenant_id
663
# enable_dhcp() could also use this
666
# 1) avoid race between restart_dhcps
667
# 2) avoid race between update_dhcp_hostfile_with_texts
668
@utils.synchronized('quantum-restart-dhcp')
669
def _atomic_restart_dhcp(self, context, dev, hosts, network_ref):
670
self.driver.update_dhcp_hostfile_with_text(dev, hosts)
671
# Since we only update hosts file, explict kill_dhcp() isn't needed.
672
# restart_dhcp() will reload hostfile if dnsmasq is already running,
673
# or start new dnsmasq
674
self.driver.restart_dhcp(context, dev, network_ref)
676
# TODO(bgh): At some point we should consider merging enable_dhcp() and
678
# TODO(tr3buchet): agree, i'm curious why they differ even now..
679
def update_dhcp(self, context, ipam_tenant_id, network_ref, vif_ref,
681
# Figure out what subnet corresponds to this network/vif
682
subnets = self.ipam.get_subnets_by_net_id(context,
683
ipam_tenant_id, network_ref['uuid'], vif_ref['uuid'])
684
for subnet in subnets:
687
# Fill in some of the network fields that we would have
688
# previously gotten from the network table (they'll be
689
# passed to the linux_net functions).
691
network_ref['cidr'] = subnet['cidr']
692
n = netaddr.IPNetwork(network_ref['cidr'])
693
network_ref['dhcp_server'] = netaddr.IPAddress(n.first + 1)
694
network_ref['dhcp_start'] = netaddr.IPAddress(n.first + 2)
695
network_ref['broadcast'] = netaddr.IPAddress(n.broadcast)
696
network_ref['gateway'] = netaddr.IPAddress(n.first + 1)
697
dev = self.driver.get_dev(network_ref)
698
# And remove the dhcp mappings for the subnet
699
hosts = self.get_dhcp_hosts_text(context,
700
subnet['network_id'], project_id)
702
self._atomic_restart_dhcp(context, dev, hosts, network_ref)
704
def validate_networks(self, context, networks):
705
"""Validates that this tenant has quantum networks with the associated
706
UUIDs. This is called by the 'os-create-server-ext' API extension
707
code so that we can return an API error code to the caller if they
708
request an invalid network.
713
project_id = context.project_id
714
for (net_id, _i) in networks:
715
# TODO(bgh): At some point we should figure out whether or
716
# not we want the verify_subnet_exists call to be optional.
717
if not self.ipam.verify_subnet_exists(context, project_id,
719
raise exception.NetworkNotFound(network_id=net_id)
720
is_tenant_net = self.q_conn.network_exists(project_id, net_id)
721
is_provider_net = self.q_conn.network_exists(
722
FLAGS.quantum_default_tenant_id,
724
if not (is_tenant_net or is_provider_net):
725
raise exception.NetworkNotFound(network_id=net_id)
727
def get_dhcp_hosts_text(self, context, subnet_id, project_id=None):
728
ips = self.ipam.get_allocated_ips(context, subnet_id, project_id)
730
admin_context = context.elevated()
733
vif = db.virtual_interface_get_by_uuid(admin_context, vif_id)
734
mac_address = vif['address']
735
text = "%s,%s.%s,%s\n" % (mac_address, "host-" + address,
736
FLAGS.dhcp_domain, address)
738
LOG.debug("DHCP hosts: %s" % hosts_text)
741
def get_dhcp_leases(self, context, network_ref):
742
"""Return a network's hosts config in dnsmasq leasefile format."""
743
subnet_id = network_ref['uuid']
744
project_id = network_ref['project_id']
745
ips = self.ipam.get_allocated_ips(context, subnet_id, project_id)
747
admin_context = context.elevated()
750
vif = db.virtual_interface_get_by_uuid(admin_context, vif_id)
751
mac_address = vif['address']
752
text = ("%s %s %s %s *\n" % (int(time.time()) -
753
FLAGS.dhcp_lease_time,
754
mac_address, address, '*'))
756
LOG.debug("DHCP leases: %s" % leases_text)
759
def setup_networks_on_host(self, *args, **kwargs):
760
# no host specific setup is needed in quantum manager