Revision e6a42a96 snf-cyclades-app/synnefo/db/models.py
b/snf-cyclades-app/synnefo/db/models.py | ||
---|---|---|
36 | 36 |
from hashlib import sha1 |
37 | 37 |
from synnefo.api.faults import ServiceUnavailable |
38 | 38 |
from synnefo.util.rapi import GanetiRapiClient |
39 |
from synnefo import settings as snf_settings |
|
39 | 40 |
|
40 | 41 |
|
41 | 42 |
BACKEND_CLIENTS = {} #{hash:Backend client} |
... | ... | |
391 | 392 |
return u'%s: %s' % (self.meta_key, self.meta_value) |
392 | 393 |
|
393 | 394 |
|
395 |
|
|
394 | 396 |
class Network(models.Model): |
395 |
NETWORK_STATES = ( |
|
397 |
OPER_STATES = ( |
|
398 |
('PENDING', 'Pending'), |
|
396 | 399 |
('ACTIVE', 'Active'), |
397 |
('DELETED', 'Deleted') |
|
400 |
('DELETED', 'Deleted'), |
|
401 |
('ERROR', 'Error') |
|
402 |
) |
|
403 |
|
|
404 |
ACTIONS = ( |
|
405 |
('CREATE', 'Create Network'), |
|
406 |
('DESTROY', 'Destroy Network'), |
|
407 |
) |
|
408 |
|
|
409 |
# The list of possible operations on the backend |
|
410 |
BACKEND_OPCODES = ( |
|
411 |
('OP_NETWORK_ADD', 'Create Network'), |
|
412 |
('OP_NETWORK_CONNECT', 'Activate Network'), |
|
413 |
('OP_NETWORK_DISCONNECT', 'Deactivate Network'), |
|
414 |
('OP_NETWORK_REMOVE', 'Remove Network') |
|
415 |
) |
|
416 |
|
|
417 |
# A backend job may be in one of the following possible states |
|
418 |
BACKEND_STATUSES = ( |
|
419 |
('success', 'request completed successfully'), |
|
420 |
('error', 'request returned error') |
|
421 |
) |
|
422 |
|
|
423 |
# The operating state of a Network, |
|
424 |
# upon the successful completion of a backend operation. |
|
425 |
# IMPORTANT: Make sure all keys have a corresponding |
|
426 |
# entry in BACKEND_OPCODES if you update this field, see #1035, #1111. |
|
427 |
OPER_STATE_FROM_OPCODE = { |
|
428 |
'OP_NETWORK_ADD': 'PENDING', |
|
429 |
'OP_NETWORK_CONNECT': 'ACTIVE', |
|
430 |
'OP_NETWORK_DISCONNECT': 'PENDING', |
|
431 |
'OP_NETWORK_REMOVE': 'DELETED' |
|
432 |
} |
|
433 |
|
|
434 |
RSAPI_STATE_FROM_OPER_STATE = { |
|
435 |
'PENDING': 'PENDING', |
|
436 |
'ACTIVE': 'ACTIVE', |
|
437 |
'DELETED': 'DELETED', |
|
438 |
'ERROR': 'ERROR' |
|
439 |
} |
|
440 |
|
|
441 |
NETWORK_TYPES = ( |
|
442 |
('PUBLIC_ROUTED', 'Public routed network'), |
|
443 |
('PRIVATE_VLAN', 'Private vlan network'), |
|
444 |
('PRIVATE_FILTERED', 'Private network with mac-filtering') |
|
398 | 445 |
) |
399 | 446 |
|
400 |
name = models.CharField(max_length=255) |
|
447 |
NETWORK_TAGS = { |
|
448 |
'PUBLIC_ROUTED': ['public-routed'], |
|
449 |
'PRIVATE_VLAN': ['private-vlan'], |
|
450 |
'PRIVATE_FILTERED': ['private-filtered'] |
|
451 |
} |
|
452 |
|
|
453 |
name = models.CharField('Network Name', max_length=128) |
|
454 |
userid = models.CharField('User ID of the owner', max_length=128, null=True) |
|
455 |
subnet = models.CharField('Subnet', max_length=32, default='10.0.0.0/24') |
|
456 |
gateway = models.CharField('Gateway', max_length=32, null=True) |
|
457 |
dhcp = models.BooleanField('DHCP', default=True) |
|
458 |
type = models.CharField(choices=NETWORK_TYPES, max_length=50, default='PRIVATE_VLAN') |
|
459 |
link = models.CharField('Network Link', max_length=128, null=True) |
|
460 |
mac_prefix = models.CharField('MAC Prefix', max_length=128, null=True) |
|
461 |
public = models.BooleanField(default=False) |
|
401 | 462 |
created = models.DateTimeField(auto_now_add=True) |
402 | 463 |
updated = models.DateTimeField(auto_now=True) |
403 |
userid = models.CharField('User ID of the owner', max_length=100, |
|
404 |
null=True) |
|
405 |
state = models.CharField(choices=NETWORK_STATES, max_length=30) |
|
406 |
public = models.BooleanField(default=False) |
|
407 |
link = models.ForeignKey('NetworkLink', related_name='+') |
|
464 |
deleted = models.BooleanField('Deleted', default=False) |
|
465 |
state = models.CharField(choices=OPER_STATES, max_length=30, default='PENDING') |
|
408 | 466 |
machines = models.ManyToManyField(VirtualMachine, |
409 | 467 |
through='NetworkInterface') |
410 | 468 |
|
469 |
action = models.CharField(choices=ACTIONS, max_length=30, null=True) |
|
470 |
backendjobid = models.PositiveIntegerField(null=True) |
|
471 |
backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30, |
|
472 |
null=True) |
|
473 |
backendjobstatus = models.CharField(choices=BACKEND_STATUSES, |
|
474 |
max_length=30, null=True) |
|
475 |
backendlogmsg = models.TextField(null=True) |
|
476 |
backendtime = models.DateTimeField(default=datetime.datetime.min) |
|
477 |
|
|
478 |
|
|
479 |
class InvalidBackendIdError(Exception): |
|
480 |
def __init__(self, value): |
|
481 |
self.value = value |
|
482 |
def __str__(self): |
|
483 |
return repr(self.value) |
|
484 |
|
|
485 |
|
|
486 |
class InvalidBackendMsgError(Exception): |
|
487 |
def __init__(self, opcode, status): |
|
488 |
self.opcode = opcode |
|
489 |
self.status = status |
|
490 |
def __str__(self): |
|
491 |
return repr('<opcode: %s, status: %s>' % (self.opcode, |
|
492 |
self.status)) |
|
493 |
|
|
494 |
class InvalidActionError(Exception): |
|
495 |
def __init__(self, action): |
|
496 |
self._action = action |
|
497 |
def __str__(self): |
|
498 |
return repr(str(self._action)) |
|
499 |
|
|
500 |
def __init__(self, *args, **kw): |
|
501 |
"""Initialize state for just created Network instances.""" |
|
502 |
super(Network, self).__init__(*args, **kw) |
|
503 |
# This gets called BEFORE an instance gets save()d for |
|
504 |
# the first time. |
|
505 |
if not self.pk: |
|
506 |
self.action = None |
|
507 |
self.backendjobid = None |
|
508 |
self.backendjobstatus = None |
|
509 |
self.backendopcode = None |
|
510 |
self.backendlogmsg = None |
|
511 |
self.backendtime = datetime.datetime.min |
|
512 |
|
|
513 |
@property |
|
514 |
def backend_id(self): |
|
515 |
"""Returns the backend id for this Network by prepending backend-prefix.""" |
|
516 |
if not self.id: |
|
517 |
raise Network.InvalidBackendIdError("self.id is None") |
|
518 |
return '%s%s' % (settings.BACKEND_PREFIX_ID, self.id) |
|
519 |
|
|
520 |
@property |
|
521 |
def backend_tag(self): |
|
522 |
"""Return the network tag to be used in backend |
|
523 |
|
|
524 |
""" |
|
525 |
return Network.NETWORK_TAGS[self.type] |
|
526 |
|
|
411 | 527 |
def __unicode__(self): |
412 | 528 |
return self.name |
413 | 529 |
|
... | ... | |
434 | 550 |
return '%s@%s' % (self.machine.name, self.network.name) |
435 | 551 |
|
436 | 552 |
|
437 |
class NetworkLink(models.Model):
|
|
438 |
network = models.ForeignKey(Network, null=True, related_name='+')
|
|
439 |
index = models.IntegerField() |
|
440 |
name = models.CharField(max_length=255)
|
|
441 |
available = models.BooleanField(default=True)
|
|
553 |
class Pool(models.Model):
|
|
554 |
available = models.BooleanField(default=True, null=False)
|
|
555 |
index = models.IntegerField(null=False, unique=True)
|
|
556 |
value = models.CharField(max_length=128, null=False, unique=True)
|
|
557 |
max_index = 0
|
|
442 | 558 |
|
443 |
def __unicode__(self): |
|
444 |
return self.name |
|
445 |
|
|
446 |
class NotAvailable(Exception): |
|
559 |
class Meta: |
|
560 |
abstract = True |
|
561 |
ordering = ['index'] |
|
562 |
|
|
563 |
@classmethod |
|
564 |
def get_available(cls): |
|
565 |
try: |
|
566 |
entry = cls.objects.filter(available=True)[0] |
|
567 |
entry.available = False |
|
568 |
entry.save() |
|
569 |
return entry |
|
570 |
except IndexError: |
|
571 |
return cls.generate_new() |
|
572 |
|
|
573 |
@classmethod |
|
574 |
def generate_new(cls): |
|
575 |
try: |
|
576 |
last = cls.objects.order_by('-index')[0] |
|
577 |
index = last.index + 1 |
|
578 |
except IndexError: |
|
579 |
index = 1 |
|
580 |
|
|
581 |
if index <= cls.max_index: |
|
582 |
return cls.objects.create(index=index, |
|
583 |
value=cls.value_from_index(index), |
|
584 |
available=False) |
|
585 |
|
|
586 |
raise Pool.PoolExhausted() |
|
587 |
|
|
588 |
@classmethod |
|
589 |
def set_available(cls, value): |
|
590 |
entry = cls.objects.get(value=value) |
|
591 |
entry.available = True |
|
592 |
entry.save() |
|
593 |
|
|
594 |
|
|
595 |
class PoolExhausted(Exception): |
|
447 | 596 |
pass |
448 | 597 |
|
598 |
|
|
599 |
class BridgePool(Pool): |
|
600 |
max_index = snf_settings.GANETI_MAX_LINK_NUMBER |
|
601 |
|
|
602 |
@staticmethod |
|
603 |
def value_from_index(index): |
|
604 |
return snf_settings.GANETI_LINK_PREFIX + str(index) |
|
605 |
|
|
606 |
|
|
607 |
class MacPrefixPool(Pool): |
|
608 |
max_index = snf_settings.GANETI_MAX_MAC_PREFIX_NUMBER |
|
609 |
|
|
610 |
@staticmethod |
|
611 |
def value_from_index(index): |
|
612 |
"""Convert number to mac prefix |
|
613 |
|
|
614 |
""" |
|
615 |
high = snf_settings.GANETI_BASE_MAC_PREFIX |
|
616 |
a = hex(int(high.replace(":", ""), 16) + index).replace("0x", '') |
|
617 |
mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)]) |
|
618 |
return mac_prefix |
Also available in: Unified diff