1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2010 United States Government as represented by the
4
# Administrator of the National Aeronautics and Space Administration.
7
# Licensed under the Apache License, Version 2.0 (the "License"); you may
8
# not use this file except in compliance with the License. You may obtain
9
# a copy of the License at
11
# http://www.apache.org/licenses/LICENSE-2.0
13
# Unless required by applicable law or agreed to in writing, software
14
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
# License for the specific language governing permissions and limitations
19
"""Network Hosts are responsible for allocating ips and setting up network.
21
There are multiple backend drivers that handle specific types of networking
22
topologies. All of the network commands are issued to a subclass of
23
:class:`NetworkManager`.
27
:network_driver: Driver to use for network creation
28
:flat_network_bridge: Bridge device for simple network instances
29
:flat_interface: FlatDhcp will bridge into this interface if set
30
:flat_network_dns: Dns for simple network
31
:vlan_start: First VLAN for private networks
32
:vpn_ip: Public IP for the cloudpipe VPN servers
33
:vpn_start: First Vpn port for private networks
34
:cnt_vpn_clients: Number of addresses reserved for vpn clients
35
:network_size: Number of addresses in each private subnet
36
:floating_range: Floating IP address block
37
:fixed_range: Fixed IP address block
38
:date_dhcp_on_disassociate: Whether to update dhcp when fixed_ip
40
:fixed_ip_disassociate_timeout: Seconds after which a deallocated ip
42
:create_unique_mac_address_attempts: Number of times to attempt creating
52
from eventlet import greenpool
54
from nova import context
56
from nova import exception
57
from nova import flags
59
from nova import log as logging
60
from nova import manager
61
from nova import quota
62
from nova import utils
64
from nova.network import api as network_api
65
from nova.compute import api as compute_api
69
LOG = logging.getLogger("nova.network.manager")
73
flags.DEFINE_string('flat_network_bridge', None,
74
'Bridge for simple network instances')
75
flags.DEFINE_string('flat_network_dns', '8.8.4.4',
76
'Dns for simple network')
77
flags.DEFINE_bool('flat_injected', False,
78
'Whether to attempt to inject network setup into guest')
79
flags.DEFINE_string('flat_interface', None,
80
'FlatDhcp will bridge into this interface if set')
81
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
82
flags.DEFINE_string('vlan_interface', None,
83
'vlans will bridge into this interface if set')
84
flags.DEFINE_integer('num_networks', 1, 'Number of networks to support')
85
flags.DEFINE_string('vpn_ip', '$my_ip',
86
'Public IP for the cloudpipe VPN servers')
87
flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
88
flags.DEFINE_bool('multi_host', False,
89
'Default value for multi_host in networks')
90
flags.DEFINE_integer('network_size', 256,
91
'Number of addresses in each private subnet')
92
flags.DEFINE_string('floating_range', '4.4.4.0/24',
93
'Floating IP address block')
94
flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
95
flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
96
flags.DEFINE_string('gateway_v6', None, 'Default IPv6 gateway')
97
flags.DEFINE_integer('cnt_vpn_clients', 0,
98
'Number of addresses reserved for vpn clients')
99
flags.DEFINE_string('network_driver', 'nova.network.linux_net',
100
'Driver to use for network creation')
101
flags.DEFINE_bool('update_dhcp_on_disassociate', False,
102
'Whether to update dhcp when fixed_ip is disassociated')
103
flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,
104
'Seconds after which a deallocated ip is disassociated')
105
flags.DEFINE_integer('create_unique_mac_address_attempts', 5,
106
'Number of attempts to create unique mac address')
107
flags.DEFINE_bool('auto_assign_floating_ip', False,
108
'Autoassigning floating ip to VM')
109
flags.DEFINE_string('network_host', socket.gethostname(),
110
'Network host to use for ip allocation in flat modes')
111
flags.DEFINE_bool('fake_call', False,
112
'If True, skip using the queue and make local calls')
113
flags.DEFINE_bool('force_dhcp_release', False,
114
'If True, send a dhcp release on instance termination')
117
class AddressAlreadyAllocated(exception.Error):
118
"""Address was already allocated."""
122
class RPCAllocateFixedIP(object):
123
"""Mixin class originally for FlatDCHP and VLAN network managers.
125
used since they share code to RPC.call allocate_fixed_ip on the
126
correct network host to configure dnsmasq
128
def _allocate_fixed_ips(self, context, instance_id, host, networks,
130
"""Calls allocate_fixed_ip once for each network."""
131
green_pool = greenpool.GreenPool()
133
vpn = kwargs.get('vpn')
134
requested_networks = kwargs.get('requested_networks')
136
for network in networks:
138
if requested_networks is not None:
139
for address in (fixed_ip for (uuid, fixed_ip) in \
140
requested_networks if network['uuid'] == uuid):
143
# NOTE(vish): if we are not multi_host pass to the network host
144
if not network['multi_host']:
145
host = network['host']
146
# NOTE(vish): if there is no network host, set one
148
host = rpc.call(context, FLAGS.network_topic,
149
{'method': 'set_network_host',
150
'args': {'network_ref': network}})
151
if host != self.host:
152
# need to call allocate_fixed_ip to correct network host
153
topic = self.db.queue_get_for(context,
157
args['instance_id'] = instance_id
158
args['network_id'] = network['id']
159
args['address'] = address
162
green_pool.spawn_n(rpc.call, context, topic,
163
{'method': '_rpc_allocate_fixed_ip',
166
# i am the correct host, run here
167
self.allocate_fixed_ip(context, instance_id, network,
168
vpn=vpn, address=address)
170
# wait for all of the allocates (if any) to finish
173
def _rpc_allocate_fixed_ip(self, context, instance_id, network_id,
175
"""Sits in between _allocate_fixed_ips and allocate_fixed_ip to
176
perform network lookup on the far side of rpc.
178
network = self.db.network_get(context, network_id)
179
self.allocate_fixed_ip(context, instance_id, network, **kwargs)
182
class FloatingIP(object):
183
"""Mixin class for adding floating IP functionality to a manager."""
184
def init_host_floating_ips(self):
185
"""Configures floating ips owned by host."""
187
admin_context = context.get_admin_context()
189
floating_ips = self.db.floating_ip_get_all_by_host(admin_context,
191
except exception.NotFound:
194
for floating_ip in floating_ips:
195
if floating_ip.get('fixed_ip', None):
196
fixed_address = floating_ip['fixed_ip']['address']
197
# NOTE(vish): The False here is because we ignore the case
198
# that the ip is already bound.
199
self.driver.bind_floating_ip(floating_ip['address'], False)
200
self.driver.ensure_floating_forward(floating_ip['address'],
203
def allocate_for_instance(self, context, **kwargs):
204
"""Handles allocating the floating IP resources for an instance.
206
calls super class allocate_for_instance() as well
208
rpc.called by network_api
210
instance_id = kwargs.get('instance_id')
211
project_id = kwargs.get('project_id')
212
requested_networks = kwargs.get('requested_networks')
213
LOG.debug(_("floating IP allocation for instance |%s|"), instance_id,
215
# call the next inherited class's allocate_for_instance()
216
# which is currently the NetworkManager version
217
# do this first so fixed ip is already allocated
218
ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
219
if FLAGS.auto_assign_floating_ip:
220
# allocate a floating ip (public_ip is just the address string)
221
public_ip = self.allocate_floating_ip(context, project_id)
222
# set auto_assigned column to true for the floating ip
223
self.db.floating_ip_set_auto_assigned(context, public_ip)
224
# get the floating ip object from public_ip string
225
floating_ip = self.db.floating_ip_get_by_address(context,
228
# get the first fixed_ip belonging to the instance
229
fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
230
fixed_ip = fixed_ips[0] if fixed_ips else None
232
# call to correct network host to associate the floating ip
233
self.network_api.associate_floating_ip(context,
236
affect_auto_assigned=True)
239
def deallocate_for_instance(self, context, **kwargs):
240
"""Handles deallocating floating IP resources for an instance.
242
calls super class deallocate_for_instance() as well.
244
rpc.called by network_api
246
instance_id = kwargs.get('instance_id')
247
LOG.debug(_("floating IP deallocation for instance |%s|"), instance_id,
250
fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
251
# add to kwargs so we can pass to super to save a db lookup there
252
kwargs['fixed_ips'] = fixed_ips
253
for fixed_ip in fixed_ips:
254
# disassociate floating ips related to fixed_ip
255
for floating_ip in fixed_ip.floating_ips:
256
address = floating_ip['address']
257
self.network_api.disassociate_floating_ip(context, address)
258
# deallocate if auto_assigned
259
if floating_ip['auto_assigned']:
260
self.network_api.release_floating_ip(context,
264
# call the next inherited class's deallocate_for_instance()
265
# which is currently the NetworkManager version
266
# call this after so floating IPs are handled first
267
super(FloatingIP, self).deallocate_for_instance(context, **kwargs)
269
def allocate_floating_ip(self, context, project_id):
270
"""Gets an floating ip from the pool."""
271
# NOTE(tr3buchet): all networks hosts in zone now use the same pool
272
LOG.debug("QUOTA: %s" % quota.allowed_floating_ips(context, 1))
273
if quota.allowed_floating_ips(context, 1) < 1:
274
LOG.warn(_('Quota exceeded for %s, tried to allocate '
277
raise quota.QuotaError(_('Address quota exceeded. You cannot '
278
'allocate any more addresses'))
279
# TODO(vish): add floating ips through manage command
280
return self.db.floating_ip_allocate_address(context,
283
def associate_floating_ip(self, context, floating_address, fixed_address):
284
"""Associates an floating ip to a fixed ip."""
285
floating_ip = self.db.floating_ip_get_by_address(context,
287
if floating_ip['fixed_ip']:
288
raise exception.FloatingIpAlreadyInUse(
289
address=floating_ip['address'],
290
fixed_ip=floating_ip['fixed_ip']['address'])
292
self.db.floating_ip_fixed_ip_associate(context,
296
self.driver.bind_floating_ip(floating_address)
297
self.driver.ensure_floating_forward(floating_address, fixed_address)
299
def disassociate_floating_ip(self, context, floating_address):
300
"""Disassociates a floating ip."""
301
fixed_address = self.db.floating_ip_disassociate(context,
303
self.driver.unbind_floating_ip(floating_address)
304
self.driver.remove_floating_forward(floating_address, fixed_address)
306
def deallocate_floating_ip(self, context, floating_address):
307
"""Returns an floating ip to the pool."""
308
self.db.floating_ip_deallocate(context, floating_address)
311
class NetworkManager(manager.SchedulerDependentManager):
312
"""Implements common network manager functionality.
314
This class must be subclassed to support specific topologies.
317
hosts configure themselves for networks they are assigned to in the
318
table upon startup. If there are networks in the table which do not
319
have hosts, those will be filled in and have hosts configured
320
as the hosts pick them up one at time during their periodic task.
321
The one at a time part is to flatten the layout to help scale
324
# If True, this manager requires VIF to create a bridge.
325
SHOULD_CREATE_BRIDGE = False
327
# If True, this manager requires VIF to create VLAN tag.
328
SHOULD_CREATE_VLAN = False
330
timeout_fixed_ips = True
332
def __init__(self, network_driver=None, *args, **kwargs):
333
if not network_driver:
334
network_driver = FLAGS.network_driver
335
self.driver = utils.import_object(network_driver)
336
self.network_api = network_api.API()
337
self.compute_api = compute_api.API()
338
super(NetworkManager, self).__init__(service_name='network',
341
@utils.synchronized('get_dhcp')
342
def _get_dhcp_ip(self, context, network_ref, host=None):
343
"""Get the proper dhcp address to listen on."""
344
# NOTE(vish): this is for compatibility
345
if not network_ref['multi_host']:
346
return network_ref['gateway']
350
network_id = network_ref['id']
352
fip = self.db.fixed_ip_get_by_network_host(context,
355
return fip['address']
356
except exception.FixedIpNotFoundForNetworkHost:
357
elevated = context.elevated()
358
return self.db.fixed_ip_associate_pool(elevated,
363
"""Do any initialization that needs to be run if this is a
366
# NOTE(vish): Set up networks for which this host already has
368
ctxt = context.get_admin_context()
369
for network in self.db.network_get_all_by_host(ctxt, self.host):
370
self._setup_network(ctxt, network)
372
def periodic_tasks(self, context=None):
373
"""Tasks to be run at a periodic interval."""
374
super(NetworkManager, self).periodic_tasks(context)
375
if self.timeout_fixed_ips:
377
timeout = FLAGS.fixed_ip_disassociate_timeout
378
time = now - datetime.timedelta(seconds=timeout)
379
num = self.db.fixed_ip_disassociate_all_by_timeout(context,
383
LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num)
385
def set_network_host(self, context, network_ref):
386
"""Safely sets the host of the network."""
387
LOG.debug(_('setting network host'), context=context)
388
host = self.db.network_set_host(context,
393
def _do_trigger_security_group_members_refresh_for_instance(self,
395
admin_context = context.get_admin_context()
396
instance_ref = self.db.instance_get(admin_context, instance_id)
397
groups = instance_ref['security_groups']
398
group_ids = [group['id'] for group in groups]
399
self.compute_api.trigger_security_group_members_refresh(admin_context,
402
def _get_networks_for_instance(self, context, instance_id, project_id,
403
requested_networks=None):
404
"""Determine & return which networks an instance should connect to."""
405
# TODO(tr3buchet) maybe this needs to be updated in the future if
406
# there is a better way to determine which networks
407
# a non-vlan instance should connect to
408
if requested_networks is not None and len(requested_networks) != 0:
409
network_uuids = [uuid for (uuid, fixed_ip) in requested_networks]
410
networks = self.db.network_get_all_by_uuids(context,
414
networks = self.db.network_get_all(context)
415
except exception.NoNetworksFound:
417
# return only networks which are not vlan networks
418
return [network for network in networks if
421
def allocate_for_instance(self, context, **kwargs):
422
"""Handles allocating the various network resources for an instance.
424
rpc.called by network_api
426
instance_id = kwargs.pop('instance_id')
427
host = kwargs.pop('host')
428
project_id = kwargs.pop('project_id')
429
type_id = kwargs.pop('instance_type_id')
430
requested_networks = kwargs.get('requested_networks')
431
vpn = kwargs.pop('vpn')
432
admin_context = context.elevated()
433
LOG.debug(_("network allocations for instance %s"), instance_id,
435
networks = self._get_networks_for_instance(admin_context,
436
instance_id, project_id,
437
requested_networks=requested_networks)
438
self._allocate_mac_addresses(context, instance_id, networks)
439
self._allocate_fixed_ips(admin_context, instance_id,
440
host, networks, vpn=vpn,
441
requested_networks=requested_networks)
442
return self.get_instance_nw_info(context, instance_id, type_id, host)
444
def deallocate_for_instance(self, context, **kwargs):
445
"""Handles deallocating various network resources for an instance.
447
rpc.called by network_api
448
kwargs can contain fixed_ips to circumvent another db lookup
450
instance_id = kwargs.pop('instance_id')
452
fixed_ips = kwargs.get('fixed_ips') or \
453
self.db.fixed_ip_get_by_instance(context, instance_id)
454
except exception.FixedIpNotFoundForInstance:
456
LOG.debug(_("network deallocation for instance |%s|"), instance_id,
458
# deallocate fixed ips
459
for fixed_ip in fixed_ips:
460
self.deallocate_fixed_ip(context, fixed_ip['address'], **kwargs)
462
# deallocate vifs (mac addresses)
463
self.db.virtual_interface_delete_by_instance(context, instance_id)
465
def get_instance_nw_info(self, context, instance_id,
466
instance_type_id, host):
467
"""Creates network info list for instance.
469
called by allocate_for_instance and netowrk_api
470
context needs to be elevated
471
:returns: network info list [(network,info),(network,info)...]
472
where network = dict containing pertinent data from a network db object
473
and info = dict containing pertinent networking data
475
# TODO(tr3buchet) should handle floating IPs as well?
477
fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
478
except exception.FixedIpNotFoundForInstance:
479
LOG.warn(_('No fixed IPs for instance %s'), instance_id)
482
vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
483
flavor = self.db.instance_type_get(context, instance_type_id)
485
# a vif has an address, instance_id, and network_id
486
# it is also joined to the instance and network given by those IDs
488
network = vif['network']
493
# determine which of the instance's IPs belong to this network
494
network_IPs = [fixed_ip['address'] for fixed_ip in fixed_ips if
495
fixed_ip['network_id'] == network['id']]
497
# TODO(tr3buchet) eventually "enabled" should be determined
501
'netmask': network['netmask'],
506
'ip': ipv6.to_global(network['cidr_v6'],
508
network['project_id']),
509
'netmask': network['netmask_v6'],
512
'bridge': network['bridge'],
514
'cidr': network['cidr'],
515
'cidr_v6': network['cidr_v6'],
516
'injected': network['injected'],
517
'vlan': network['vlan'],
518
'bridge_interface': network['bridge_interface'],
519
'multi_host': network['multi_host']}
520
if network['multi_host']:
521
dhcp_server = self._get_dhcp_ip(context, network, host)
523
dhcp_server = self._get_dhcp_ip(context,
527
'label': network['label'],
528
'gateway': network['gateway'],
529
'dhcp_server': dhcp_server,
530
'broadcast': network['broadcast'],
531
'mac': vif['address'],
532
'vif_uuid': vif['uuid'],
533
'rxtx_cap': flavor['rxtx_cap'],
535
'ips': [ip_dict(ip) for ip in network_IPs],
536
'should_create_bridge': self.SHOULD_CREATE_BRIDGE,
537
'should_create_vlan': self.SHOULD_CREATE_VLAN}
539
if network['cidr_v6']:
540
info['ip6s'] = [ip6_dict()]
541
# TODO(tr3buchet): handle ip6 routes here as well
542
if network['gateway_v6']:
543
info['gateway6'] = network['gateway_v6']
545
info['dns'].append(network['dns1'])
547
info['dns'].append(network['dns2'])
549
network_info.append((network_dict, info))
552
def _allocate_mac_addresses(self, context, instance_id, networks):
553
"""Generates mac addresses and creates vif rows in db for them."""
554
for network in networks:
555
self.add_virtual_interface(context, instance_id, network['id'])
557
def add_virtual_interface(self, context, instance_id, network_id):
558
vif = {'address': self.generate_mac_address(),
559
'instance_id': instance_id,
560
'network_id': network_id,
561
'uuid': str(utils.gen_uuid())}
562
# try FLAG times to create a vif record with a unique mac_address
563
for _ in xrange(FLAGS.create_unique_mac_address_attempts):
565
return self.db.virtual_interface_create(context, vif)
566
except exception.VirtualInterfaceCreateException:
567
vif['address'] = self.generate_mac_address()
569
self.db.virtual_interface_delete_by_instance(context,
571
raise exception.VirtualInterfaceMacAddressException()
573
def generate_mac_address(self):
574
"""Generate an Ethernet MAC address."""
575
mac = [0x02, 0x16, 0x3e,
576
random.randint(0x00, 0x7f),
577
random.randint(0x00, 0xff),
578
random.randint(0x00, 0xff)]
579
return ':'.join(map(lambda x: "%02x" % x, mac))
581
def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
582
"""Adds a fixed ip to an instance from specified network."""
583
networks = [self.db.network_get(context, network_id)]
584
self._allocate_fixed_ips(context, instance_id, host, networks)
586
def remove_fixed_ip_from_instance(self, context, instance_id, address):
587
"""Removes a fixed ip from an instance from specified network."""
588
fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
589
for fixed_ip in fixed_ips:
590
if fixed_ip['address'] == address:
591
self.deallocate_fixed_ip(context, address)
593
raise exception.FixedIpNotFoundForSpecificInstance(
594
instance_id=instance_id, ip=address)
596
def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
597
"""Gets a fixed ip from the pool."""
598
# TODO(vish): when this is called by compute, we can associate compute
599
# with a network, or a cluster of computes with a network
600
# and use that network here with a method like
601
# network_get_by_compute_host
604
address = kwargs.get('address', None)
606
address = self.db.fixed_ip_associate(context,
607
address, instance_id,
610
address = self.db.fixed_ip_associate_pool(context.elevated(),
613
self._do_trigger_security_group_members_refresh_for_instance(
615
get_vif = self.db.virtual_interface_get_by_instance_and_network
616
vif = get_vif(context, instance_id, network['id'])
617
values = {'allocated': True,
618
'virtual_interface_id': vif['id']}
619
self.db.fixed_ip_update(context, address, values)
621
self._setup_network(context, network)
624
def deallocate_fixed_ip(self, context, address, **kwargs):
625
"""Returns a fixed ip to the pool."""
626
self.db.fixed_ip_update(context, address,
628
'virtual_interface_id': None})
629
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
630
instance_ref = fixed_ip_ref['instance']
631
instance_id = instance_ref['id']
632
self._do_trigger_security_group_members_refresh_for_instance(
634
if FLAGS.force_dhcp_release:
635
dev = self.driver.get_dev(fixed_ip_ref['network'])
636
vif = self.db.virtual_interface_get_by_instance_and_network(
637
context, instance_ref['id'], fixed_ip_ref['network']['id'])
638
self.driver.release_dhcp(dev, address, vif['address'])
640
def lease_fixed_ip(self, context, address):
641
"""Called by dhcp-bridge when ip is leased."""
642
LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
643
fixed_ip = self.db.fixed_ip_get_by_address(context, address)
644
instance = fixed_ip['instance']
646
raise exception.Error(_('IP %s leased that is not associated') %
649
self.db.fixed_ip_update(context,
653
if not fixed_ip['allocated']:
654
LOG.warn(_('IP |%s| leased that isn\'t allocated'), address,
657
def release_fixed_ip(self, context, address):
658
"""Called by dhcp-bridge when ip is released."""
659
LOG.debug(_('Released IP |%(address)s|'), locals(), context=context)
660
fixed_ip = self.db.fixed_ip_get_by_address(context, address)
661
instance = fixed_ip['instance']
663
raise exception.Error(_('IP %s released that is not associated') %
665
if not fixed_ip['leased']:
666
LOG.warn(_('IP %s released that was not leased'), address,
668
self.db.fixed_ip_update(context,
671
if not fixed_ip['allocated']:
672
self.db.fixed_ip_disassociate(context, address)
673
# NOTE(vish): dhcp server isn't updated until next setup, this
674
# means there will stale entries in the conf file
675
# the code below will update the file if necessary
676
if FLAGS.update_dhcp_on_disassociate:
677
network_ref = self.db.fixed_ip_get_network(context, address)
678
self._setup_network(context, network_ref)
680
def create_networks(self, context, label, cidr, multi_host, num_networks,
681
network_size, cidr_v6, gateway_v6, bridge,
682
bridge_interface, dns1=None, dns2=None, **kwargs):
683
"""Create networks based on parameters."""
684
# NOTE(jkoelker): these are dummy values to make sure iter works
685
fixed_net_v4 = netaddr.IPNetwork('0/32')
686
fixed_net_v6 = netaddr.IPNetwork('::0/128')
690
subnet_bits = int(math.ceil(math.log(network_size, 2)))
693
fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
694
prefixlen_v6 = 128 - subnet_bits
695
subnets_v6 = fixed_net_v6.subnet(prefixlen_v6, count=num_networks)
698
fixed_net_v4 = netaddr.IPNetwork(cidr)
699
prefixlen_v4 = 32 - subnet_bits
700
subnets_v4 = list(fixed_net_v4.subnet(prefixlen_v4,
703
# NOTE(jkoelker): This replaces the _validate_cidrs call and
704
# prevents looping multiple times
706
nets = self.db.network_get_all(context)
707
except exception.NoNetworksFound:
709
used_subnets = [netaddr.IPNetwork(net['cidr']) for net in nets]
711
def find_next(subnet):
712
next_subnet = subnet.next()
713
while next_subnet in subnets_v4:
714
next_subnet = next_subnet.next()
715
if next_subnet in fixed_net_v4:
718
for subnet in list(subnets_v4):
719
if subnet in used_subnets:
720
next_subnet = find_next(subnet)
722
subnets_v4.remove(subnet)
723
subnets_v4.append(next_subnet)
726
raise ValueError(_('cidr already in use'))
727
for used_subnet in used_subnets:
728
if subnet in used_subnet:
729
msg = _('requested cidr (%(cidr)s) conflicts with '
730
'existing supernet (%(super)s)')
731
raise ValueError(msg % {'cidr': subnet,
732
'super': used_subnet})
733
if used_subnet in subnet:
734
next_subnet = find_next(subnet)
736
subnets_v4.remove(subnet)
737
subnets_v4.append(next_subnet)
740
msg = _('requested cidr (%(cidr)s) conflicts '
741
'with existing smaller cidr '
743
raise ValueError(msg % {'cidr': subnet,
744
'smaller': used_subnet})
747
subnets = itertools.izip_longest(subnets_v4, subnets_v6)
748
for index, (subnet_v4, subnet_v6) in enumerate(subnets):
750
net['bridge'] = bridge
751
net['bridge_interface'] = bridge_interface
752
net['multi_host'] = multi_host
758
net['label'] = '%s_%d' % (label, index)
762
if cidr and subnet_v4:
763
net['cidr'] = str(subnet_v4)
764
net['netmask'] = str(subnet_v4.netmask)
765
net['gateway'] = str(subnet_v4[1])
766
net['broadcast'] = str(subnet_v4.broadcast)
767
net['dhcp_start'] = str(subnet_v4[2])
769
if cidr_v6 and subnet_v6:
770
net['cidr_v6'] = str(subnet_v6)
772
# use a pre-defined gateway if one is provided
773
net['gateway_v6'] = str(gateway_v6)
775
net['gateway_v6'] = str(subnet_v6[1])
777
net['netmask_v6'] = str(subnet_v6._prefixlen)
779
if kwargs.get('vpn', False):
780
# this bit here is for vlan-manager
783
vlan = kwargs['vlan_start'] + index
784
net['vpn_private_address'] = str(subnet_v4[2])
785
net['dhcp_start'] = str(subnet_v4[3])
787
net['bridge'] = 'br%s' % vlan
789
# NOTE(vish): This makes ports unique accross the cloud, a more
790
# robust solution would be to make them uniq per ip
791
net['vpn_public_port'] = kwargs['vpn_start'] + index
793
# None if network with cidr or cidr_v6 already exists
794
network = self.db.network_create_safe(context, net)
797
raise ValueError(_('Network already exists!'))
799
networks.append(network)
801
if network and cidr and subnet_v4:
802
self._create_fixed_ips(context, network['id'])
805
def delete_network(self, context, fixed_range, require_disassociated=True):
807
network = db.network_get_by_cidr(context, fixed_range)
809
if require_disassociated and network.project_id is not None:
810
raise ValueError(_('Network must be disassociated from project %s'
811
' before delete' % network.project_id))
812
db.network_delete_safe(context, network.id)
815
def _bottom_reserved_ips(self): # pylint: disable=R0201
816
"""Number of reserved ips at the bottom of the range."""
817
return 2 # network, gateway
820
def _top_reserved_ips(self): # pylint: disable=R0201
821
"""Number of reserved ips at the top of the range."""
824
def _create_fixed_ips(self, context, network_id):
825
"""Create all fixed ips for network."""
826
network = self.db.network_get(context, network_id)
827
# NOTE(vish): Should these be properties of the network as opposed
828
# to properties of the manager class?
829
bottom_reserved = self._bottom_reserved_ips
830
top_reserved = self._top_reserved_ips
831
project_net = netaddr.IPNetwork(network['cidr'])
832
num_ips = len(project_net)
833
for index in range(num_ips):
834
address = str(project_net[index])
835
if index < bottom_reserved or num_ips - index < top_reserved:
839
self.db.fixed_ip_create(context, {'network_id': network_id,
841
'reserved': reserved})
843
def _allocate_fixed_ips(self, context, instance_id, host, networks,
845
"""Calls allocate_fixed_ip once for each network."""
846
raise NotImplementedError()
848
def _setup_network(self, context, network_ref):
849
"""Sets up network on this host."""
850
raise NotImplementedError()
852
def validate_networks(self, context, networks):
853
"""check if the networks exists and host
854
is set to each network.
856
if networks is None or len(networks) == 0:
859
network_uuids = [uuid for (uuid, fixed_ip) in networks]
861
self._get_networks_by_uuids(context, network_uuids)
863
for network_uuid, address in networks:
864
# check if the fixed IP address is valid and
865
# it actually belongs to the network
866
if address is not None:
867
if not utils.is_valid_ipv4(address):
868
raise exception.FixedIpInvalid(address=address)
870
fixed_ip_ref = self.db.fixed_ip_get_by_address(context,
872
if fixed_ip_ref['network']['uuid'] != network_uuid:
873
raise exception.FixedIpNotFoundForNetwork(address=address,
874
network_uuid=network_uuid)
875
if fixed_ip_ref['instance'] is not None:
876
raise exception.FixedIpAlreadyInUse(address=address)
878
def _get_networks_by_uuids(self, context, network_uuids):
879
return self.db.network_get_all_by_uuids(context, network_uuids)
882
class FlatManager(NetworkManager):
883
"""Basic network where no vlans are used.
885
FlatManager does not do any bridge or vlan creation. The user is
886
responsible for setting up whatever bridges are specified when creating
887
networks through nova-manage. This bridge needs to be created on all
890
The idea is to create a single network for the host with a command like:
891
nova-manage network create 192.168.0.0/24 1 256. Creating multiple
892
networks for for one manager is currently not supported, but could be
893
added by modifying allocate_fixed_ip and get_network to get the a network
894
with new logic instead of network_get_by_bridge. Arbitrary lists of
895
addresses in a single network can be accomplished with manual db editing.
897
If flat_injected is True, the compute host will attempt to inject network
898
config into the guest. It attempts to modify /etc/network/interfaces and
899
currently only works on debian based systems. To support a wider range of
900
OSes, some other method may need to be devised to let the guest know which
901
ip it should be using so that it can configure itself. Perhaps an attached
902
disk or serial device with configuration info.
904
Metadata forwarding must be handled by the gateway, and since nova does
905
not do any setup in this mode, it must be done manually. Requests to
906
169.254.169.254 port 80 will need to be forwarded to the api server.
910
timeout_fixed_ips = False
912
def _allocate_fixed_ips(self, context, instance_id, host, networks,
914
"""Calls allocate_fixed_ip once for each network."""
915
requested_networks = kwargs.get('requested_networks')
916
for network in networks:
918
if requested_networks is not None:
919
for address in (fixed_ip for (uuid, fixed_ip) in \
920
requested_networks if network['uuid'] == uuid):
923
self.allocate_fixed_ip(context, instance_id,
924
network, address=address)
926
def deallocate_fixed_ip(self, context, address, **kwargs):
927
"""Returns a fixed ip to the pool."""
928
super(FlatManager, self).deallocate_fixed_ip(context, address,
930
self.db.fixed_ip_disassociate(context, address)
932
def _setup_network(self, context, network_ref):
933
"""Setup Network on this host."""
935
net['injected'] = FLAGS.flat_injected
936
self.db.network_update(context, network_ref['id'], net)
939
class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
940
"""Flat networking with dhcp.
942
FlatDHCPManager will start up one dhcp server to give out addresses.
943
It never injects network settings into the guest. It also manages bridges.
944
Otherwise it behaves like FlatManager.
948
SHOULD_CREATE_BRIDGE = True
951
"""Do any initialization that needs to be run if this is a
954
self.driver.init_host()
955
self.driver.ensure_metadata_ip()
957
super(FlatDHCPManager, self).init_host()
958
self.init_host_floating_ips()
960
self.driver.metadata_forward()
962
def _setup_network(self, context, network_ref):
963
"""Sets up network on this host."""
964
network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
966
mac_address = self.generate_mac_address()
967
dev = self.driver.plug(network_ref, mac_address)
968
self.driver.initialize_gateway_device(dev, network_ref)
970
if not FLAGS.fake_network:
971
self.driver.update_dhcp(context, dev, network_ref)
973
self.driver.update_ra(context, dev, network_ref)
974
gateway = utils.get_my_linklocal(dev)
975
self.db.network_update(context, network_ref['id'],
976
{'gateway_v6': gateway})
979
class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
980
"""Vlan network with dhcp.
982
VlanManager is the most complicated. It will create a host-managed
983
vlan for each project. Each project gets its own subnet. The networks
984
and associated subnets are created with nova-manage using a command like:
985
nova-manage network create 10.0.0.0/8 3 16. This will create 3 networks
986
of 16 addresses from the beginning of the 10.0.0.0 range.
988
A dhcp server is run for each subnet, so each project will have its own.
989
For this mode to be useful, each project will need a vpn to access the
990
instances in its subnet.
994
SHOULD_CREATE_BRIDGE = True
995
SHOULD_CREATE_VLAN = True
998
"""Do any initialization that needs to be run if this is a
1002
self.driver.init_host()
1003
self.driver.ensure_metadata_ip()
1005
NetworkManager.init_host(self)
1006
self.init_host_floating_ips()
1008
self.driver.metadata_forward()
1010
def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
1011
"""Gets a fixed ip from the pool."""
1012
if kwargs.get('vpn', None):
1013
address = network['vpn_private_address']
1014
self.db.fixed_ip_associate(context,
1019
address = kwargs.get('address', None)
1021
address = self.db.fixed_ip_associate(context, address,
1025
address = self.db.fixed_ip_associate_pool(context,
1028
self._do_trigger_security_group_members_refresh_for_instance(
1030
vif = self.db.virtual_interface_get_by_instance_and_network(context,
1033
values = {'allocated': True,
1034
'virtual_interface_id': vif['id']}
1035
self.db.fixed_ip_update(context, address, values)
1036
self._setup_network(context, network)
1039
def add_network_to_project(self, context, project_id):
1040
"""Force adds another network to a project."""
1041
self.db.network_associate(context, project_id, force=True)
1043
def _get_networks_for_instance(self, context, instance_id, project_id,
1044
requested_networks=None):
1045
"""Determine which networks an instance should connect to."""
1046
# get networks associated with project
1047
if requested_networks is not None and len(requested_networks) != 0:
1048
network_uuids = [uuid for (uuid, fixed_ip) in requested_networks]
1049
networks = self.db.network_get_all_by_uuids(context,
1053
networks = self.db.project_get_networks(context, project_id)
1056
def create_networks(self, context, **kwargs):
1057
"""Create networks based on parameters."""
1058
# Check that num_networks + vlan_start is not > 4094, fixes lp708025
1059
if kwargs['num_networks'] + kwargs['vlan_start'] > 4094:
1060
raise ValueError(_('The sum between the number of networks and'
1061
' the vlan start cannot be greater'
1064
# check that num networks and network size fits in fixed_net
1065
fixed_net = netaddr.IPNetwork(kwargs['cidr'])
1066
if len(fixed_net) < kwargs['num_networks'] * kwargs['network_size']:
1067
raise ValueError(_('The network range is not big enough to fit '
1068
'%(num_networks)s. Network size is %(network_size)s') %
1071
NetworkManager.create_networks(self, context, vpn=True, **kwargs)
1073
def _setup_network(self, context, network_ref):
1074
"""Sets up network on this host."""
1075
if not network_ref['vpn_public_address']:
1077
address = FLAGS.vpn_ip
1078
net['vpn_public_address'] = address
1079
network_ref = db.network_update(context, network_ref['id'], net)
1081
address = network_ref['vpn_public_address']
1082
network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
1084
mac_address = self.generate_mac_address()
1085
dev = self.driver.plug(network_ref, mac_address)
1086
self.driver.initialize_gateway_device(dev, network_ref)
1088
# NOTE(vish): only ensure this forward if the address hasn't been set
1090
if address == FLAGS.vpn_ip and hasattr(self.driver,
1091
"ensure_vpn_forward"):
1092
self.driver.ensure_vpn_forward(FLAGS.vpn_ip,
1093
network_ref['vpn_public_port'],
1094
network_ref['vpn_private_address'])
1095
if not FLAGS.fake_network:
1096
self.driver.update_dhcp(context, dev, network_ref)
1098
self.driver.update_ra(context, dev, network_ref)
1099
gateway = utils.get_my_linklocal(dev)
1100
self.db.network_update(context, network_ref['id'],
1101
{'gateway_v6': gateway})
1103
def _get_networks_by_uuids(self, context, network_uuids):
1104
return self.db.network_get_all_by_uuids(context, network_uuids,
1108
def _bottom_reserved_ips(self):
1109
"""Number of reserved ips at the bottom of the range."""
1110
return super(VlanManager, self)._bottom_reserved_ips + 1 # vpn server
1113
def _top_reserved_ips(self):
1114
"""Number of reserved ips at the top of the range."""
1115
parent_reserved = super(VlanManager, self)._top_reserved_ips
1116
return parent_reserved + FLAGS.cnt_vpn_clients