Revision 77f0fa63
b/snf-cyclades-app/synnefo/api/actions.py | ||
---|---|---|
34 | 34 |
from socket import getfqdn |
35 | 35 |
from vncauthproxy.client import request_forwarding as request_vnc_forwarding |
36 | 36 |
|
37 |
from django.db import transaction |
|
37 | 38 |
from django.conf import settings |
38 | 39 |
from django.http import HttpResponse |
39 | 40 |
from django.template.loader import render_to_string |
... | ... | |
42 | 43 |
from synnefo.api.faults import (BadRequest, ServiceUnavailable, |
43 | 44 |
ItemNotFound, BuildInProgress) |
44 | 45 |
from synnefo.api.util import random_password, get_vm, get_nic_from_index |
45 |
from synnefo.db.models import NetworkInterface |
|
46 |
from synnefo.logic import backend |
|
46 |
from synnefo.db.models import NetworkInterface, Network
|
|
47 |
from synnefo.logic import backend, ippool
|
|
47 | 48 |
from synnefo.logic.utils import get_rsapi_state |
48 | 49 |
|
49 | 50 |
|
... | ... | |
289 | 290 |
|
290 | 291 |
|
291 | 292 |
@network_action('add') |
293 |
@transaction.commit_on_success |
|
292 | 294 |
def add(request, net, args): |
293 | 295 |
# Normal Response Code: 202 |
294 | 296 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
303 | 305 |
if not server_id: |
304 | 306 |
raise BadRequest('Malformed Request.') |
305 | 307 |
vm = get_vm(server_id, request.user_uniq) |
306 |
backend.connect_to_network(vm, net) |
|
308 |
|
|
309 |
#Dummy write to enforce isolation |
|
310 |
Network.objects.filter(id=net.id).update(id=net.id) |
|
311 |
net = Network.objects.get(id=net.id) |
|
312 |
# Get a free IP from the address pool. |
|
313 |
pool = ippool.IPPool(net) |
|
314 |
try: |
|
315 |
address = pool.get_free_address() |
|
316 |
except ippool.IPPool.IPPoolExhausted: |
|
317 |
raise ServiceUnavailable('Network is full') |
|
318 |
pool.save() |
|
319 |
|
|
320 |
backend.connect_to_network(vm, net, address) |
|
307 | 321 |
return HttpResponse(status=202) |
308 | 322 |
|
323 |
|
|
309 | 324 |
@network_action('remove') |
310 | 325 |
def remove(request, net, args): |
311 | 326 |
# Normal Response Code: 202 |
b/snf-cyclades-app/synnefo/db/migrations/0045_auto__chg_field_networkinterface_mac__add_field_network_reservations.py | ||
---|---|---|
1 |
# encoding: utf-8 |
|
2 |
import datetime |
|
3 |
from south.db import db |
|
4 |
from south.v2 import SchemaMigration |
|
5 |
from django.db import models |
|
6 |
|
|
7 |
class Migration(SchemaMigration): |
|
8 |
|
|
9 |
def forwards(self, orm): |
|
10 |
|
|
11 |
# Changing field 'NetworkInterface.mac' |
|
12 |
db.alter_column('db_networkinterface', 'mac', self.gf('django.db.models.fields.CharField')(max_length=32, null=True)) |
|
13 |
|
|
14 |
# Adding field 'Network.reservations' |
|
15 |
db.add_column('db_network', 'reservations', self.gf('django.db.models.fields.TextField')(default=''), keep_default=False) |
|
16 |
|
|
17 |
|
|
18 |
def backwards(self, orm): |
|
19 |
|
|
20 |
# Changing field 'NetworkInterface.mac' |
|
21 |
db.alter_column('db_networkinterface', 'mac', self.gf('django.db.models.fields.CharField')(max_length=17, null=True)) |
|
22 |
|
|
23 |
# Deleting field 'Network.reservations' |
|
24 |
db.delete_column('db_network', 'reservations') |
|
25 |
|
|
26 |
|
|
27 |
models = { |
|
28 |
'db.backend': { |
|
29 |
'Meta': {'object_name': 'Backend'}, |
|
30 |
'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), |
|
31 |
'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
32 |
'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
33 |
'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
34 |
'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
35 |
'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}), |
|
36 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
37 |
'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
38 |
'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
39 |
'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
40 |
'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), |
|
41 |
'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
|
42 |
'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}), |
|
43 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
44 |
'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}) |
|
45 |
}, |
|
46 |
'db.backendnetwork': { |
|
47 |
'Meta': {'object_name': 'BackendNetwork'}, |
|
48 |
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}), |
|
49 |
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), |
|
50 |
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
51 |
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
|
52 |
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
53 |
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}), |
|
54 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
55 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
56 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
57 |
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}), |
|
58 |
'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}), |
|
59 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
|
60 |
}, |
|
61 |
'db.bridgepool': { |
|
62 |
'Meta': {'object_name': 'BridgePool'}, |
|
63 |
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
|
64 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
65 |
'index': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), |
|
66 |
'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) |
|
67 |
}, |
|
68 |
'db.flavor': { |
|
69 |
'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'}, |
|
70 |
'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
71 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
72 |
'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
73 |
'disk_template': ('django.db.models.fields.CharField', [], {'default': "'drbd'", 'max_length': '32'}), |
|
74 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
75 |
'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'}) |
|
76 |
}, |
|
77 |
'db.macprefixpool': { |
|
78 |
'Meta': {'object_name': 'MacPrefixPool'}, |
|
79 |
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
|
80 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
81 |
'index': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), |
|
82 |
'value': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) |
|
83 |
}, |
|
84 |
'db.network': { |
|
85 |
'Meta': {'object_name': 'Network'}, |
|
86 |
'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), |
|
87 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
88 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
89 |
'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
|
90 |
'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
|
91 |
'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), |
|
92 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
93 |
'link': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), |
|
94 |
'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
|
95 |
'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}), |
|
96 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
|
97 |
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
98 |
'reservations': ('django.db.models.fields.TextField', [], {'default': "''"}), |
|
99 |
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}), |
|
100 |
'subnet': ('django.db.models.fields.CharField', [], {'default': "'10.0.0.0/24'", 'max_length': '32'}), |
|
101 |
'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), |
|
102 |
'type': ('django.db.models.fields.CharField', [], {'default': "'PRIVATE_PHYSICAL_VLAN'", 'max_length': '50'}), |
|
103 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
|
104 |
'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}) |
|
105 |
}, |
|
106 |
'db.networkinterface': { |
|
107 |
'Meta': {'object_name': 'NetworkInterface'}, |
|
108 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
109 |
'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
110 |
'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
111 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
112 |
'index': ('django.db.models.fields.IntegerField', [], {}), |
|
113 |
'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}), |
|
114 |
'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), |
|
115 |
'mac': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
|
116 |
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}), |
|
117 |
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}), |
|
118 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
|
119 |
}, |
|
120 |
'db.virtualmachine': { |
|
121 |
'Meta': {'object_name': 'VirtualMachine'}, |
|
122 |
'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
123 |
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}), |
|
124 |
'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), |
|
125 |
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), |
|
126 |
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
127 |
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
|
128 |
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
129 |
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}), |
|
130 |
'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
|
131 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
|
132 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
133 |
'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}), |
|
134 |
'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
135 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
136 |
'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
|
137 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
|
138 |
'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
|
139 |
'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
|
140 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
|
141 |
'userid': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
|
142 |
}, |
|
143 |
'db.virtualmachinemetadata': { |
|
144 |
'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'}, |
|
145 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
|
146 |
'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), |
|
147 |
'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}), |
|
148 |
'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"}) |
|
149 |
} |
|
150 |
} |
|
151 |
|
|
152 |
complete_apps = ['db'] |
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.logic.ippool import IPPool |
|
39 | 40 |
from synnefo import settings as snf_settings |
40 | 41 |
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield |
41 | 42 |
|
... | ... | |
407 | 408 |
return u'%s: %s' % (self.meta_key, self.meta_value) |
408 | 409 |
|
409 | 410 |
|
410 |
|
|
411 | 411 |
class Network(models.Model): |
412 | 412 |
OPER_STATES = ( |
413 | 413 |
('PENDING', 'Pending'), |
... | ... | |
436 | 436 |
('CUSTOM_BRIDGED', 'Custom bridged network') |
437 | 437 |
) |
438 | 438 |
|
439 |
|
|
440 | 439 |
name = models.CharField('Network Name', max_length=128) |
441 | 440 |
userid = models.CharField('User ID of the owner', max_length=128, null=True) |
442 | 441 |
subnet = models.CharField('Subnet', max_length=32, default='10.0.0.0/24') |
... | ... | |
459 | 458 |
action = models.CharField(choices=ACTIONS, max_length=32, null=True, |
460 | 459 |
default=None) |
461 | 460 |
|
461 |
reservations = models.TextField(default='') |
|
462 |
|
|
463 |
ip_pool = None |
|
464 |
|
|
465 |
|
|
462 | 466 |
class InvalidBackendIdError(Exception): |
463 | 467 |
def __init__(self, value): |
464 | 468 |
self.value = value |
... | ... | |
538 | 542 |
for back in Backend.objects.all(): |
539 | 543 |
BackendNetwork.objects.create(backend=back, network=self) |
540 | 544 |
|
545 |
@property |
|
546 |
def pool(self): |
|
547 |
if self.ip_pool: |
|
548 |
return self.ip_pool |
|
549 |
else: |
|
550 |
self.ip_pool = IPPool(self) |
|
551 |
return self.ip_pool |
|
552 |
|
|
553 |
def reserve_address(self, address, pool=None): |
|
554 |
pool = pool or self.pool |
|
555 |
pool.reserve(address) |
|
556 |
pool._update_network() |
|
557 |
self.save() |
|
558 |
|
|
559 |
def release_address(self, address, pool=None): |
|
560 |
pool = pool or self.pool |
|
561 |
pool.release(address) |
|
562 |
pool._update_network() |
|
563 |
self.save() |
|
564 |
|
|
565 |
# def get_free_address(self): |
|
566 |
# # Get yourself inside a transaction |
|
567 |
# network = Network.objects.get(id=self.id) |
|
568 |
# # Get the pool object |
|
569 |
# pool = network.pool |
|
570 |
# print network is self |
|
571 |
# try: |
|
572 |
# address = pool.get_free_address() |
|
573 |
# except IPPoolExhausted: |
|
574 |
# raise Network.NetworkIsFull |
|
575 |
|
|
576 |
# pool._update_network() |
|
577 |
# network.save() |
|
578 |
# return address |
|
579 |
|
|
580 |
# class NetworkIsFull(Exception): |
|
581 |
# pass |
|
582 |
|
|
541 | 583 |
|
542 | 584 |
class BackendNetwork(models.Model): |
543 | 585 |
OPER_STATES = ( |
... | ... | |
609 | 651 |
created = models.DateTimeField(auto_now_add=True) |
610 | 652 |
updated = models.DateTimeField(auto_now=True) |
611 | 653 |
index = models.IntegerField(null=False) |
612 |
mac = models.CharField(max_length=17, null=True)
|
|
654 |
mac = models.CharField(max_length=32, null=True)
|
|
613 | 655 |
ipv4 = models.CharField(max_length=15, null=True) |
614 | 656 |
ipv6 = models.CharField(max_length=100, null=True) |
615 | 657 |
firewall_profile = models.CharField(choices=FIREWALL_PROFILES, |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
114 | 114 |
Update the state of the VM in the DB accordingly. |
115 | 115 |
""" |
116 | 116 |
|
117 |
vm.nics.all().delete() |
|
118 |
for i, nic in enumerate(nics): |
|
119 |
network = nic.get('network', '') |
|
117 |
old_nics = vm.nics.order_by('index') |
|
118 |
new_nics = enumerate(nics) |
|
119 |
|
|
120 |
networks = {} |
|
121 |
|
|
122 |
for old_nic in old_nics: |
|
123 |
pk = old_nic.network.pk |
|
124 |
# Get the cached Network or get it from DB |
|
125 |
if pk in networks: |
|
126 |
net = networks[pk] |
|
127 |
else: |
|
128 |
#Dummy write to enforce isolation |
|
129 |
Network.objects.filter(id=pk).update(id=pk) |
|
130 |
net = Network.objects.get(pk=pk) |
|
131 |
net.release_address(old_nic.ipv4) |
|
132 |
old_nic.delete() |
|
133 |
|
|
134 |
for i, new_nic in new_nics: |
|
135 |
network = new_nic.get('network', '') |
|
120 | 136 |
n = str(network) |
121 |
if n == settings.GANETI_PUBLIC_NETWORK: |
|
122 |
net = Network.objects.get(public=True) |
|
137 |
pk = utils.id_from_network_name(n) |
|
138 |
|
|
139 |
# Get the cached Network or get it from DB |
|
140 |
if pk in networks: |
|
141 |
net = networks[pk] |
|
123 | 142 |
else: |
124 |
pk = utils.id_from_network_name(n) |
|
125 |
net = Network.objects.get(id=pk) |
|
143 |
net = Network.objects.get(pk=pk) |
|
144 |
|
|
145 |
# Get the new nic info |
|
146 |
mac = new_nic.get('mac', '') |
|
147 |
ipv4 = new_nic.get('ip', '') |
|
148 |
ipv6 = new_nic.get('ipv6', '') |
|
126 | 149 |
|
127 |
firewall = nic.get('firewall', '') |
|
150 |
firewall = new_nic.get('firewall', '')
|
|
128 | 151 |
firewall_profile = _reverse_tags.get(firewall, '') |
129 | 152 |
if not firewall_profile and net.public: |
130 | 153 |
firewall_profile = settings.DEFAULT_FIREWALL_PROFILE |
131 | 154 |
|
155 |
if ipv4: |
|
156 |
net.reserve_address(ipv4) |
|
157 |
|
|
132 | 158 |
vm.nics.create( |
133 | 159 |
network=net, |
134 | 160 |
index=i, |
135 |
mac=nic.get('mac', ''),
|
|
136 |
ipv4=nic.get('ip', ''),
|
|
137 |
ipv6=nic.get('ipv6', ''),
|
|
161 |
mac=mac,
|
|
162 |
ipv4=ipv4,
|
|
163 |
ipv6=ipv6,
|
|
138 | 164 |
firewall_profile=firewall_profile, |
139 | 165 |
dirty=False) |
140 | 166 |
|
141 | 167 |
vm.backendtime = etime |
142 | 168 |
vm.save() |
143 |
net.save() |
|
144 | 169 |
|
145 | 170 |
|
146 | 171 |
@transaction.commit_on_success |
... | ... | |
490 | 515 |
backend.client.DeleteNetwork(network.backend_id, jobs) |
491 | 516 |
|
492 | 517 |
|
493 |
def connect_to_network(vm, network): |
|
518 |
def connect_to_network(vm, network, address):
|
|
494 | 519 |
"""Connect a virtual machine to a network. |
495 | 520 |
|
496 | 521 |
@param vm: VirtualMachine object |
... | ... | |
498 | 523 |
|
499 | 524 |
""" |
500 | 525 |
|
501 |
ip = network.dhcp and 'pool' or None |
|
526 |
# ip = network.dhcp and 'pool' or None
|
|
502 | 527 |
|
503 |
nic = {'ip': ip, 'network': network.backend_id}
|
|
528 |
nic = {'ip': address, 'network': network.backend_id}
|
|
504 | 529 |
vm.client.ModifyInstance(vm.backend_vm_id, nics=[('add', nic)], |
505 | 530 |
hotplug=True, dry_run=settings.TEST) |
506 | 531 |
|
b/snf-cyclades-app/synnefo/logic/ippool.py | ||
---|---|---|
1 |
import ipaddr |
|
2 |
|
|
3 |
from bitarray import bitarray |
|
4 |
from base64 import b64encode, b64decode |
|
5 |
|
|
6 |
|
|
7 |
class IPPool(object): |
|
8 |
def __init__(self, network): |
|
9 |
self.net = network |
|
10 |
self.network = ipaddr.IPNetwork(self.net.subnet) |
|
11 |
|
|
12 |
gateway = self.net.gateway |
|
13 |
self.gateway = gateway and ipaddr.IPAddress(gateway) or None |
|
14 |
|
|
15 |
if self.net.reservations: |
|
16 |
self.reservations = bitarray() |
|
17 |
self.reservations.fromstring(b64decode(self.net.reservations)) |
|
18 |
else: |
|
19 |
numhosts = self.network.numhosts |
|
20 |
self.reservations = bitarray(numhosts) |
|
21 |
self.reservations.setall(False) |
|
22 |
self.reservations[0] = True |
|
23 |
self.reservations[numhosts - 1] = True |
|
24 |
# if self.net.type == 'PUBLIC_ROUTED': |
|
25 |
# self.reservations[numhosts - 2] = True |
|
26 |
|
|
27 |
def _contains(self, address): |
|
28 |
if address is None: |
|
29 |
return False |
|
30 |
addr = ipaddr.IPAddress(address) |
|
31 |
|
|
32 |
return (addr in self.network) |
|
33 |
|
|
34 |
def _address_index(self, address): |
|
35 |
"""Convert IP address to bitarray index |
|
36 |
|
|
37 |
""" |
|
38 |
if not self._contains(address): |
|
39 |
raise Exception("%s does not contain %s" % (self.network, address)) |
|
40 |
addr = ipaddr.IPAddress(address) |
|
41 |
|
|
42 |
return int(addr) - int(self.network.network) |
|
43 |
|
|
44 |
def _mark(self, address, value): |
|
45 |
index = self._address_index(address) |
|
46 |
self.reservations[index] = value |
|
47 |
|
|
48 |
def reserve(self, address): |
|
49 |
self._mark(address, True) |
|
50 |
|
|
51 |
def release(self, address): |
|
52 |
self._mark(address, False) |
|
53 |
|
|
54 |
def is_reserved(self, address): |
|
55 |
index = self._address_index(address) |
|
56 |
return self.reservations[index] |
|
57 |
|
|
58 |
def is_full(self): |
|
59 |
return self.reservations.all() |
|
60 |
|
|
61 |
def count_reserved(self): |
|
62 |
return self.reservations.count(True) |
|
63 |
|
|
64 |
def count_free(self): |
|
65 |
return self.reservations.count(False) |
|
66 |
|
|
67 |
def get_map(self): |
|
68 |
return self.reservations.to01().replace("1", "X").replace("0", ".") |
|
69 |
|
|
70 |
def get_free_address(self): |
|
71 |
if self.is_full(): |
|
72 |
raise Exception("%s if full", self.network) |
|
73 |
|
|
74 |
index = self.reservations.index(False) |
|
75 |
address = str(self.network[index]) |
|
76 |
self.reserve(address) |
|
77 |
return address |
|
78 |
|
|
79 |
def save(self): |
|
80 |
self._update_network() |
|
81 |
self.net.save() |
|
82 |
|
|
83 |
def _update_network(self): |
|
84 |
self.net.reservations = b64encode(self.reservations.tostring()) |
|
85 |
|
|
86 |
class IPPoolExhausted(Exception): |
|
87 |
pass |
Also available in: Unified diff