~anotherjesse/nova/remove-vendor

« back to all changes in this revision

Viewing changes to nova/compute/network.py

  • Committer: Vishvananda Ishaya
  • Date: 2010-06-29 05:47:02 UTC
  • mfrom: (118.1.6)
  • Revision ID: git-v1:1ae74170a99cc8ca0e1cce6a1448f8a7bb9c600b
Merge remote branch 'angst/apply_api' into diekeeper

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
import nova.exception
35
35
from nova.compute import exception
36
36
from nova import flags
 
37
from nova.compute import model
37
38
from nova import utils
38
39
from nova.auth import users
39
40
 
60
61
 
61
62
logging.getLogger().setLevel(logging.DEBUG)
62
63
 
 
64
 
 
65
class Vlan(model.BasicModel):
 
66
    def __init__(self, project, vlan):
 
67
        """
 
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.
 
70
        """
 
71
        self.project_id = project
 
72
        self.vlan_id = vlan
 
73
 
 
74
    @property
 
75
    def identifier(self):
 
76
        return "%s:%s" % (self.project_id, self.vlan_id)
 
77
 
 
78
    @classmethod
 
79
    def create(cls, project, vlan):
 
80
        instance = cls(project, vlan)
 
81
        instance.save()
 
82
        return instance
 
83
 
 
84
    @classmethod
 
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)
 
89
        if vlan:
 
90
            return cls(project, vlan)
 
91
        else:
 
92
            return None
 
93
 
 
94
    @classmethod
 
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)
 
100
 
 
101
    @classmethod
 
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__)
 
106
        rv = {}
 
107
        h = datastore.Redis.instance().hgetall(set_name)
 
108
        for v in h.keys():
 
109
            rv[h[v]] = v
 
110
        return rv
 
111
 
 
112
    @classmethod
 
113
    @model.absorb_connection_error
 
114
    def all(cls):
 
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)
 
118
 
 
119
    @model.absorb_connection_error
 
120
    def save(self):
 
121
        """
 
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".
 
125
        """
 
126
        set_name = self._redis_set_name(self.__class__.__name__)
 
127
        datastore.Redis.instance().hset(set_name, self.project_id, self.vlan_id)
 
128
 
 
129
    @model.absorb_connection_error
 
130
    def destroy(self):
 
131
        set_name = self._redis_set_name(self.__class__.__name__)
 
132
        datastore.Redis.instance().hdel(set_name, self.project)
 
133
 
 
134
    def subnet(self):
 
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])
 
140
 
63
141
# CLEANUP:
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
68
146
 
69
 
class BaseNetwork(datastore.RedisModel):
70
 
    bridge_gets_ip = False
71
 
    object_type = 'network'
72
 
 
73
 
    @classmethod
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)):
79
 
                yield addr
 
147
class BaseNetwork(model.BasicModel):
 
148
    override_type = 'network'
 
149
 
 
150
    @property
 
151
    def identifier(self):
 
152
        return self.network_id
 
153
 
 
154
    def default_state(self):
 
155
        return {'network_id': self.network_id, 'network_str': self.network_str}
80
156
 
81
157
    @classmethod
82
158
    def create(cls, user_id, project_id, security_group, vlan, network_str):
90
166
        return net
91
167
 
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__()
96
172
        self.save()
97
173
 
98
174
    @property
99
175
    def network(self):
100
176
        return IPy.IP(self['network_str'])
 
177
 
101
178
    @property
102
179
    def netmask(self):
103
180
        return self.network.netmask()
 
181
 
104
182
    @property
105
183
    def gateway(self):
106
184
        return self.network[1]
 
185
 
107
186
    @property
108
187
    def broadcast(self):
109
188
        return self.network.broadcast()
 
189
 
110
190
    @property
111
191
    def gateway(self):
112
192
        return self.network[1]
 
193
 
113
194
    @property
114
195
    def bridge_name(self):
115
196
        return "br%s" % (self["vlan"])
193
274
            netmask
194
275
    """
195
276
 
 
277
    override_type = 'network'
 
278
 
196
279
    @classmethod
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,
 
286
                          network_str)
202
287
 
203
288
    def __init__(self, *args, **kwargs):
204
289
        super(BridgedNetwork, self).__init__(*args, **kwargs)
218
303
        dhcp_range_end: the last ip to give out
219
304
    """
220
305
    bridge_gets_ip = True
 
306
    override_type = 'network'
221
307
 
222
308
    def __init__(self, *args, **kwargs):
223
309
        super(DHCPNetwork, self).__init__(*args, **kwargs)
227
313
        self.dhcp_range_end = self.network[-(1 + FLAGS.cnt_vpn_clients)]
228
314
        try:
229
315
            os.makedirs(FLAGS.networks_path)
 
316
        # NOTE(todd): I guess this is a lazy way to not have to check if the
 
317
        #             directory exists, but shouldn't we be smarter about
 
318
        #             telling the difference between existing directory and
 
319
        #             permission denied? (Errno 17 vs 13, OSError)
230
320
        except Exception, err:
231
321
            pass
232
322
 
261
351
        else:
262
352
            linux_net.start_dnsmasq(self)
263
353
 
264
 
class PublicAddress(datastore.RedisModel):
265
 
    object_type="address"
 
354
class PublicAddress(model.BasicModel):
 
355
    override_type = "address"
266
356
 
267
357
    def __init__(self, address):
268
 
        super(PublicAddress, self).__init__(address)
 
358
        self.address = address
 
359
        super(PublicAddress, self).__init__()
 
360
 
 
361
    @property
 
362
    def identifier(self):
 
363
        return self.address
 
364
 
 
365
    def default_state(self):
 
366
        return {'address': self.address}
269
367
 
270
368
    @classmethod
271
369
    def create(cls, user_id, project_id, address):
272
 
        addr = cls(address=address)
273
 
        addr['address'] = address
 
370
        addr = cls(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())
279
375
        addr.save()
280
376
        return addr
281
377
 
282
378
DEFAULT_PORTS = [("tcp",80), ("tcp",22), ("udp",1194), ("tcp",443)]
283
379
class PublicNetworkController(BaseNetwork):
 
380
    override_type = 'network'
 
381
 
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))
384
482
 
385
483
 
386
 
VLANS_KEY = "vlans"
387
 
def _add_vlan(project_id, vlan):
388
 
    datastore.Redis.instance().hset(VLANS_KEY, project_id, vlan)
389
 
 
390
 
def _rem_vlan(project_id):
391
 
    datastore.Redis.instance().hdel(VLANS_KEY, project_id)
392
 
 
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)
396
 
 
 
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):
398
487
    """
399
488
    Allocate vlan IDs to individual users.
400
489
    """
401
 
    vlan = datastore.Redis.instance().hget(VLANS_KEY, project_id)
 
490
    vlan = Vlan.lookup(project_id)
402
491
    if vlan:
403
492
        return vlan
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):
 
495
        vstr = str(vnum)
 
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)
410
 
            return 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)
414
 
            return vlan
 
500
            vlan = Vlan.lookup(old_project_id)
 
501
            if vlan:
 
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
 
509
                vlan.save()
 
510
                return vlan
 
511
            else:
 
512
                return Vlan.create(project_id, vnum)
415
513
    raise exception.AddressNotAllocated("Out of VLANs")
416
514
 
417
 
 
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)
433
530
 
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)
437
536
    if not project:
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." %
 
538
                                   project_id)
 
539
    return DHCPNetwork.get_network_for_project(project.project_manager_id,
 
540
                                               project.id, security_group)
440
541
 
441
 
def get_subnet_from_vlan(vlan):
442
 
    """Assign one subnet to each VLAN, for now."""
443
 
    vlan = int(vlan)
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])
447
542
 
448
543
def restart_nets():
449
544
    """ Ensure the network for each user is enabled"""