1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
# Copyright [2010] [Anso Labs, LLC]
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
17
Classes for network control, including VLANs, DHCP, and IP allocation.
24
# TODO(termie): clean up these imports
25
from nova import vendor
28
from nova import datastore
30
from nova.compute import exception
31
from nova import flags
32
from nova import utils
33
from nova.auth import users
38
flags.DEFINE_string('net_libvirt_xml_template',
39
utils.abspath('compute/net.libvirt.xml.template'),
40
'Template file for libvirt networks')
41
flags.DEFINE_string('networks_path', utils.abspath('../networks'),
42
'Location to keep network config files')
43
flags.DEFINE_integer('public_vlan', 1, 'VLAN for public IP addresses')
44
flags.DEFINE_string('public_interface', 'vlan1', 'Interface for public IP addresses')
45
flags.DEFINE_string('bridge_dev', 'eth1',
46
'network device for bridges')
47
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
48
flags.DEFINE_integer('vlan_end', 4093, 'Last VLAN for private networks')
49
flags.DEFINE_integer('network_size', 256, 'Number of addresses in each private subnet')
50
flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block')
51
flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block')
54
# HACK(vish): to delay _get_keeper() loading
56
if _get_keeper.keeper == None:
57
_get_keeper.keeper = datastore.Keeper(prefix="net")
58
return _get_keeper.keeper
59
_get_keeper.keeper = None
61
logging.getLogger().setLevel(logging.DEBUG)
64
# TODO(ja): use singleton for usermanager instead of self.manager in vlanpool et al
65
# TODO(ja): does vlanpool "keeper" need to know the min/max - shouldn't FLAGS always win?
67
class Network(object):
68
def __init__(self, *args, **kwargs):
69
self.bridge_gets_ip = False
71
os.makedirs(FLAGS.networks_path)
72
except Exception, err:
77
return {'vlan': self.vlan,
78
'network': self.network_str,
81
def load(self, **kwargs):
82
self.network_str = kwargs.get('network', "192.168.100.0/24")
83
self.hosts = kwargs.get('hosts', {})
84
self.vlan = kwargs.get('vlan', 100)
85
self.name = "nova-%s" % (self.vlan)
86
self.network = IPy.IP(self.network_str)
87
self.gateway = self.network[1]
88
self.netmask = self.network.netmask()
89
self.broadcast = self.network.broadcast()
90
self.bridge_name = "br%s" % (self.vlan)
93
return json.dumps(self.to_dict())
95
def __unicode__(self):
96
return json.dumps(self.to_dict())
99
def from_dict(cls, args):
100
for arg in args.keys():
103
args[str(arg)] = value
108
def from_json(cls, json_string):
109
parsed = json.loads(json_string)
110
return cls.from_dict(parsed)
113
for idx in range(3, len(self.network)-2):
114
yield self.network[idx]
116
def allocate_ip(self, user_id, mac):
117
for ip in self.range():
119
if not address in self.hosts.keys():
120
logging.debug("Allocating IP %s to %s" % (address, user_id))
121
self.hosts[address] = {
122
"address" : address, "user_id" : user_id, 'mac' : mac
124
self.express(address=address)
126
raise exception.NoMoreAddresses()
128
def deallocate_ip(self, ip_str):
129
if not ip_str in self.hosts.keys():
130
raise exception.AddressNotAllocated()
131
del self.hosts[ip_str]
132
# TODO(joshua) SCRUB from the leases file somehow
133
self.deexpress(address=ip_str)
135
def list_addresses(self):
136
for address in self.hosts.values():
139
def express(self, address=None):
142
def deexpress(self, address=None):
148
VLAN configuration, that when expressed creates the vlan
152
vlan - integer (example: 42)
153
bridge_dev - string (example: eth0)
156
def __init__(self, *args, **kwargs):
157
super(Vlan, self).__init__(*args, **kwargs)
158
self.bridge_dev = FLAGS.bridge_dev
160
def express(self, address=None):
161
super(Vlan, self).express(address=address)
163
logging.debug("Starting VLAN inteface for %s network" % (self.vlan))
164
linux_net.vlan_create(self)
169
class VirtNetwork(Vlan):
171
Virtual Network that can export libvirt configuration or express itself to
172
create a bridge (with or without an IP address/netmask/gateway)
175
bridge_name - string (example value: br42)
176
vlan - integer (example value: 42)
177
bridge_gets_ip - boolean used during bridge creation
179
if bridge_gets_ip then network address for bridge uses the properties:
185
def __init__(self, *args, **kwargs):
186
super(VirtNetwork, self).__init__(*args, **kwargs)
189
""" generate XML for libvirt network """
191
libvirt_xml = open(FLAGS.net_libvirt_xml_template).read()
192
xml_info = {'name' : self.name,
193
'bridge_name' : self.bridge_name,
194
'device' : "vlan%s" % (self.vlan),
195
'gateway' : self.gateway,
196
'netmask' : self.netmask,
198
libvirt_xml = libvirt_xml % xml_info
201
def express(self, address=None):
202
""" creates a bridge device on top of the Vlan """
203
super(VirtNetwork, self).express(address=address)
205
logging.debug("Starting Bridge inteface for %s network" % (self.vlan))
206
linux_net.bridge_create(self)
210
class DHCPNetwork(VirtNetwork):
213
dhcp_listen_address: the ip of the gateway / dhcp host
214
dhcp_range_start: the first ip to give out
215
dhcp_range_end: the last ip to give out
217
def __init__(self, *args, **kwargs):
218
super(DHCPNetwork, self).__init__(*args, **kwargs)
219
logging.debug("Initing DHCPNetwork object...")
220
self.bridge_gets_ip = True
221
self.dhcp_listen_address = self.network[1]
222
self.dhcp_range_start = self.network[3]
223
self.dhcp_range_end = self.network[-2]
225
def express(self, address=None):
226
super(DHCPNetwork, self).express(address=address)
227
if len(self.hosts.values()) > 0:
228
logging.debug("Starting dnsmasq server for network with vlan %s" % self.vlan)
229
linux_net.start_dnsmasq(self)
231
logging.debug("Not launching dnsmasq cause I don't think we have any hosts.")
233
def deexpress(self, address=None):
234
# if this is the last address, stop dns
235
super(DHCPNetwork, self).deexpress(address=address)
236
if len(self.hosts.values()) == 0:
237
linux_net.stop_dnsmasq(self)
239
linux_net.start_dnsmasq(self)
242
class PrivateNetwork(DHCPNetwork):
243
def __init__(self, **kwargs):
244
super(PrivateNetwork, self).__init__(**kwargs)
248
return {'vlan': self.vlan,
249
'network': self.network_str,
252
def express(self, *args, **kwargs):
253
super(PrivateNetwork, self).express(*args, **kwargs)
257
class PublicNetwork(Network):
258
def __init__(self, network="192.168.216.0/24", **kwargs):
259
super(PublicNetwork, self).__init__(network=network, **kwargs)
262
def allocate_ip(self, user_id, mac):
263
for ip in self.range():
265
if not address in self.hosts.keys():
266
logging.debug("Allocating IP %s to %s" % (address, user_id))
267
self.hosts[address] = {
268
"address" : address, "user_id" : user_id, 'mac' : mac
270
self.express(address=address)
272
raise exception.NoMoreAddresses()
274
def deallocate_ip(self, ip_str):
275
if not ip_str in self.hosts:
276
raise exception.AddressNotAllocated()
277
del self.hosts[ip_str]
278
# TODO(joshua) SCRUB from the leases file somehow
279
self.deexpress(address=ip_str)
281
def associate_address(self, public_ip, private_ip, instance_id):
282
if not public_ip in self.hosts:
283
raise exception.AddressNotAllocated()
284
for addr in self.hosts.values():
285
if addr.has_key('private_ip') and addr['private_ip'] == private_ip:
286
raise exception.AddressAlreadyAssociated()
287
if self.hosts[public_ip].has_key('private_ip'):
288
raise exception.AddressAlreadyAssociated()
289
self.hosts[public_ip]['private_ip'] = private_ip
290
self.hosts[public_ip]['instance_id'] = instance_id
291
self.express(address=public_ip)
293
def disassociate_address(self, public_ip):
294
if not public_ip in self.hosts:
295
raise exception.AddressNotAllocated()
296
if not self.hosts[public_ip].has_key('private_ip'):
297
raise exception.AddressNotAssociated()
298
self.deexpress(public_ip)
299
del self.hosts[public_ip]['private_ip']
300
del self.hosts[public_ip]['instance_id']
301
# TODO Express the removal
303
def deexpress(self, address):
304
addr = self.hosts[address]
305
public_ip = addr['address']
306
private_ip = addr['private_ip']
307
linux_net.remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s" % (public_ip, private_ip))
308
linux_net.remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" % (private_ip, public_ip))
309
linux_net.remove_rule("FORWARD -d %s -p icmp -j ACCEPT" % (private_ip))
310
for (protocol, port) in [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]:
311
linux_net.remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" % (private_ip, protocol, port))
313
def express(self, address=None):
314
logging.debug("Todo - need to create IPTables natting entries for this net.")
315
addresses = self.hosts.values()
317
addresses = [self.hosts[address]]
318
for addr in addresses:
319
if not addr.has_key('private_ip'):
321
public_ip = addr['address']
322
private_ip = addr['private_ip']
323
linux_net.bind_public_ip(public_ip, FLAGS.public_interface)
324
linux_net.confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s" % (public_ip, private_ip))
325
linux_net.confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" % (private_ip, public_ip))
326
# TODO: Get these from the secgroup datastore entries
327
linux_net.confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" % (private_ip))
328
for (protocol, port) in [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]:
329
linux_net.confirm_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" % (private_ip, protocol, port))
332
class NetworkPool(object):
333
# TODO - Allocations need to be system global
336
self.network = IPy.IP(FLAGS.private_range)
337
netsize = FLAGS.network_size
338
if not netsize in [4,8,16,32,64,128,256,512,1024]:
339
raise exception.NotValidNetworkSize()
340
self.netsize = netsize
341
self.startvlan = FLAGS.vlan_start
343
def get_from_vlan(self, vlan):
344
start = (vlan-self.startvlan) * self.netsize
345
net_str = "%s-%s" % (self.network[start], self.network[start + self.netsize - 1])
346
logging.debug("Allocating %s" % net_str)
350
class VlanPool(object):
351
def __init__(self, **kwargs):
352
self.start = FLAGS.vlan_start
353
self.end = FLAGS.vlan_end
354
self.vlans = kwargs.get('vlans', {})
356
self.manager = users.UserManager.instance()
357
for user_id, vlan in self.vlans.iteritems():
358
self.vlanpool[vlan] = user_id
361
return {'vlans': self.vlans}
364
return json.dumps(self.to_dict())
366
def __unicode__(self):
367
return json.dumps(self.to_dict())
370
def from_dict(cls, args):
371
for arg in args.keys():
374
args[str(arg)] = value
379
def from_json(cls, json_string):
380
parsed = json.loads(json_string)
381
return cls.from_dict(parsed)
383
def assign_vlan(self, user_id, vlan):
384
logging.debug("Assigning vlan %s to user %s" % (vlan, user_id))
385
self.vlans[user_id] = vlan
386
self.vlanpool[vlan] = user_id
387
return self.vlans[user_id]
389
def next(self, user_id):
390
for old_user_id, vlan in self.vlans.iteritems():
391
if not self.manager.get_user(old_user_id):
392
_get_keeper()["%s-default" % old_user_id] = {}
393
del _get_keeper()["%s-default" % old_user_id]
394
del self.vlans[old_user_id]
395
return self.assign_vlan(user_id, vlan)
396
vlans = self.vlanpool.keys()
397
vlans.append(self.start)
398
nextvlan = max(vlans) + 1
399
if nextvlan == self.end:
400
raise exception.AddressNotAllocated("Out of VLANs")
401
return self.assign_vlan(user_id, nextvlan)
404
class NetworkController(object):
405
""" The network controller is in charge of network connections """
407
def __init__(self, **kwargs):
408
logging.debug("Starting up the network controller.")
409
self.manager = users.UserManager.instance()
411
if not _get_keeper()['vlans']:
412
_get_keeper()['vlans'] = {}
413
if not _get_keeper()['public']:
414
_get_keeper()['public'] = {'vlan': FLAGS.public_vlan, 'network' : FLAGS.public_range}
418
_get_keeper()['public'] = {'vlan': FLAGS.public_vlan, 'network': FLAGS.public_range }
419
_get_keeper()['vlans'] = {}
420
# TODO : Get rid of old interfaces, bridges, and IPTables rules.
423
def public_net(self):
425
self._pubnet = PublicNetwork.from_dict(_get_keeper()['public'])
426
self._pubnet.load(**_get_keeper()['public'])
431
return VlanPool.from_dict(_get_keeper()['vlans'])
433
def get_network_from_name(self, network_name):
434
net_dict = _get_keeper()[network_name]
436
return PrivateNetwork.from_dict(net_dict)
439
def get_public_ip_for_instance(self, instance_id):
440
# FIXME: this should be a lookup - iteration won't scale
441
for address_record in self.describe_addresses(type=PublicNetwork):
442
if address_record.get(u'instance_id', 'free') == instance_id:
443
return address_record[u'address']
445
def get_users_network(self, user_id):
446
""" get a user's private network, allocating one if needed """
448
user = self.manager.get_user(user_id)
450
raise Exception("User %s doesn't exist, uhoh." % user_id)
451
usernet = self.get_network_from_name("%s-default" % user_id)
453
pool = self.vlan_pool
454
vlan = pool.next(user_id)
455
private_pool = NetworkPool()
456
network_str = private_pool.get_from_vlan(vlan)
457
logging.debug("Constructing network %s and %s for %s" % (network_str, vlan, user_id))
458
usernet = PrivateNetwork(
461
_get_keeper()["%s-default" % user_id] = usernet.to_dict()
462
_get_keeper()['vlans'] = pool.to_dict()
465
def allocate_address(self, user_id, mac=None, type=PrivateNetwork):
468
if type == PrivateNetwork:
469
net = self.get_users_network(user_id)
470
ip = net.allocate_ip(user_id, mac)
472
_get_keeper()["%s-default" % user_id] = net.to_dict()
474
net = self.public_net
475
ip = net.allocate_ip(user_id, mac)
477
_get_keeper()['public'] = net.to_dict()
478
return (ip, net_name)
480
def deallocate_address(self, address):
481
if address in self.public_net.network:
482
net = self.public_net
483
rv = net.deallocate_ip(str(address))
484
_get_keeper()['public'] = net.to_dict()
486
for user in self.manager.get_users():
487
if address in self.get_users_network(user.id).network:
488
net = self.get_users_network(user.id)
489
rv = net.deallocate_ip(str(address))
490
_get_keeper()["%s-default" % user.id] = net.to_dict()
492
raise exception.AddressNotAllocated()
494
def describe_addresses(self, type=PrivateNetwork):
495
if type == PrivateNetwork:
497
for user in self.manager.get_users():
498
addresses.extend(self.get_users_network(user.id).list_addresses())
500
return self.public_net.list_addresses()
502
def associate_address(self, address, private_ip, instance_id):
503
net = self.public_net
504
rv = net.associate_address(address, private_ip, instance_id)
505
_get_keeper()['public'] = net.to_dict()
508
def disassociate_address(self, address):
509
net = self.public_net
510
rv = net.disassociate_address(address)
511
_get_keeper()['public'] = net.to_dict()
514
def express(self,address=None):
515
for user in self.manager.get_users():
516
self.get_users_network(user.id).express()
518
def report_state(self):