61
62
logging.getLogger().setLevel(logging.DEBUG)
65
class Vlan(model.BasicModel):
66
def __init__(self, project, vlan):
68
Since we don't want to try and find a vlan by its identifier,
69
but by a project id, we don't call super-init.
71
self.project_id = project
76
return "%s:%s" % (self.project_id, self.vlan_id)
79
def create(cls, project, vlan):
80
instance = cls(project, vlan)
85
@model.absorb_connection_error
86
def lookup(cls, project):
87
set_name = cls._redis_set_name(cls.__name__)
88
vlan = datastore.Redis.instance().hget(set_name, project)
90
return cls(project, vlan)
95
@model.absorb_connection_error
96
def dict_by_project(cls):
97
"""a hash of project:vlan"""
98
set_name = cls._redis_set_name(cls.__name__)
99
return datastore.Redis.instance().hgetall(set_name)
102
@model.absorb_connection_error
103
def dict_by_vlan(cls):
104
"""a hash of vlan:project"""
105
set_name = cls._redis_set_name(cls.__name__)
107
h = datastore.Redis.instance().hgetall(set_name)
113
@model.absorb_connection_error
115
set_name = cls._redis_set_name(cls.__name__)
116
for project,vlan in datastore.Redis.instance().hgetall(set_name):
117
yield cls(project, vlan)
119
@model.absorb_connection_error
122
Vlan saves state into a giant hash named "vlans", with keys of
123
proejct_id and value of valn number. Therefore, we skip the
124
default way of saving into "vlan:ID" and adding to a set of "vlans".
126
set_name = self._redis_set_name(self.__class__.__name__)
127
datastore.Redis.instance().hset(set_name, self.project_id, self.vlan_id)
129
@model.absorb_connection_error
131
set_name = self._redis_set_name(self.__class__.__name__)
132
datastore.Redis.instance().hdel(set_name, self.project)
135
vlan = int(self.vlan_id)
136
network = IPy.IP(FLAGS.private_range)
137
start = (vlan-FLAGS.vlan_start) * FLAGS.network_size
138
return "%s-%s" % (network[start],
139
network[start + FLAGS.network_size - 1])
64
142
# TODO(ja): Save the IPs at the top of each subnet for cloudpipe vpn clients
65
143
# TODO(ja): use singleton for usermanager instead of self.manager in vlanpool et al
66
144
# TODO(ja): does vlanpool "keeper" need to know the min/max - shouldn't FLAGS always win?
67
145
# TODO(joshua): Save the IPs at the top of each subnet for cloudpipe vpn clients
69
class BaseNetwork(datastore.RedisModel):
70
bridge_gets_ip = False
71
object_type = 'network'
74
def get_all_hosts(cls):
75
for vlan in get_assigned_vlans().values():
76
network_str = get_subnet_from_vlan(vlan)
77
for addr in datastore.Redis.instance().hgetall(
78
"network:%s:hosts" % (network_str)):
147
class BaseNetwork(model.BasicModel):
148
override_type = 'network'
151
def identifier(self):
152
return self.network_id
154
def default_state(self):
155
return {'network_id': self.network_id, 'network_str': self.network_str}
82
158
def create(cls, user_id, project_id, security_group, vlan, network_str):
92
168
def __init__(self, network_id, network_str=None):
93
super(BaseNetwork, self).__init__(object_id=network_id)
94
self['network_id'] = network_id
95
self['network_str'] = network_str
169
self.network_id = network_id
170
self.network_str = network_str
171
super(BaseNetwork, self).__init__()
99
175
def network(self):
100
176
return IPy.IP(self['network_str'])
102
179
def netmask(self):
103
180
return self.network.netmask()
105
183
def gateway(self):
106
184
return self.network[1]
108
187
def broadcast(self):
109
188
return self.network.broadcast()
111
191
def gateway(self):
112
192
return self.network[1]
114
195
def bridge_name(self):
115
196
return "br%s" % (self["vlan"])
277
override_type = 'network'
197
280
def get_network_for_project(cls, user_id, project_id, security_group):
198
281
vlan = get_vlan_for_project(project_id)
199
network_str = get_subnet_from_vlan(vlan)
200
logging.debug("creating network on vlan %s with network string %s" % (vlan, network_str))
201
return cls.create(user_id, project_id, security_group, vlan, network_str)
282
network_str = vlan.subnet()
283
logging.debug("creating network on vlan %s with network string %s",
284
vlan.vlan_id, network_str)
285
return cls.create(user_id, project_id, security_group, vlan.vlan_id,
203
288
def __init__(self, *args, **kwargs):
204
289
super(BridgedNetwork, self).__init__(*args, **kwargs)
262
352
linux_net.start_dnsmasq(self)
264
class PublicAddress(datastore.RedisModel):
265
object_type="address"
354
class PublicAddress(model.BasicModel):
355
override_type = "address"
267
357
def __init__(self, address):
268
super(PublicAddress, self).__init__(address)
358
self.address = address
359
super(PublicAddress, self).__init__()
362
def identifier(self):
365
def default_state(self):
366
return {'address': self.address}
271
369
def create(cls, user_id, project_id, address):
272
addr = cls(address=address)
273
addr['address'] = address
274
371
addr['user_id'] = user_id
275
372
addr['project_id'] = project_id
276
373
addr['instance_id'] = 'available'
277
374
addr['private_ip'] = 'available'
278
addr["create_time"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
282
378
DEFAULT_PORTS = [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]
283
379
class PublicNetworkController(BaseNetwork):
380
override_type = 'network'
284
382
def __init__(self, *args, **kwargs):
285
383
network_id = "public:default"
286
384
super(PublicNetworkController, self).__init__(network_id, FLAGS.public_range)
383
481
% (private_ip, protocol, port))
387
def _add_vlan(project_id, vlan):
388
datastore.Redis.instance().hset(VLANS_KEY, project_id, vlan)
390
def _rem_vlan(project_id):
391
datastore.Redis.instance().hdel(VLANS_KEY, project_id)
393
def get_assigned_vlans():
394
""" Returns a dictionary, with keys of project_id and values of vlan_id """
395
return datastore.Redis.instance().hgetall(VLANS_KEY)
484
# FIXME(todd): does this present a race condition, or is there some piece of
485
# architecture that mitigates it (only one queue listener per net)?
397
486
def get_vlan_for_project(project_id):
399
488
Allocate vlan IDs to individual users.
401
vlan = datastore.Redis.instance().hget(VLANS_KEY, project_id)
490
vlan = Vlan.lookup(project_id)
404
assigned_vlans = get_assigned_vlans()
405
# TODO(joshua) I can do this in one loop, I think
406
for old_project_id, vlan in assigned_vlans.iteritems():
493
known_vlans = Vlan.dict_by_vlan()
494
for vnum in range(FLAGS.vlan_start, FLAGS.vlan_end):
496
if not known_vlans.has_key(vstr):
497
return Vlan.create(project_id, vnum)
498
old_project_id = known_vlans[vstr]
407
499
if not users.UserManager.instance().get_project(old_project_id):
408
_rem_vlan(old_project_id)
409
_add_vlan(project_id, vlan)
411
for vlan in range(FLAGS.vlan_start, FLAGS.vlan_end):
412
if not str(vlan) in assigned_vlans.values():
413
_add_vlan(project_id, vlan)
500
vlan = Vlan.lookup(old_project_id)
502
# NOTE(todd): This doesn't check for vlan id match, because
503
# it seems to be assumed that vlan<=>project is
504
# always a 1:1 mapping. It could be made way
505
# sexier if it didn't fight agains the way
506
# BasicModel worked and used associate_with
507
# to build connections to projects.
508
vlan.project_id = project_id
512
return Vlan.create(project_id, vnum)
415
513
raise exception.AddressNotAllocated("Out of VLANs")
418
515
def get_network_by_address(address):
419
516
for project in users.UserManager.instance().get_projects():
420
517
net = get_project_network(project.id)
434
531
def get_project_network(project_id, security_group='default'):
435
532
""" get a project's private network, allocating one if needed """
533
# TODO(todd): It looks goofy to get a project from a UserManager.
534
# Refactor to still use the LDAP backend, but not User specific.
436
535
project = users.UserManager.instance().get_project(project_id)
438
raise nova.exception.Error("Project %s doesn't exist, uhoh." % project_id)
439
return DHCPNetwork.get_network_for_project(project.project_manager_id, project.id, security_group)
537
raise nova.exception.Error("Project %s doesn't exist, uhoh." %
539
return DHCPNetwork.get_network_for_project(project.project_manager_id,
540
project.id, security_group)
441
def get_subnet_from_vlan(vlan):
442
"""Assign one subnet to each VLAN, for now."""
444
network = IPy.IP(FLAGS.private_range)
445
start = (vlan-FLAGS.vlan_start) * FLAGS.network_size
446
return "%s-%s" % (network[start], network[start + FLAGS.network_size - 1])
448
543
def restart_nets():
449
544
""" Ensure the network for each user is enabled"""