Revision fdc94944

b/snf-cyclades-app/synnefo/api/actions.py
42 42

  
43 43
from synnefo.api.faults import (BadRequest, ServiceUnavailable,
44 44
                                ItemNotFound, BuildInProgress)
45
from synnefo.api.util import random_password, get_vm, get_nic_from_index
45
from synnefo.api.util import (random_password, get_vm, get_nic_from_index,
46
                              get_network_free_address)
46 47
from synnefo.db.models import NetworkInterface, Network
47
from synnefo.db.pools import IPPool
48
from synnefo.db.pools import EmptyPool
48 49
from synnefo.logic import backend
49 50
from synnefo.logic.utils import get_rsapi_state
50 51

  
......
314 315
        raise ServiceUnavailable('Network not active yet')
315 316

  
316 317
    # Get a free IP from the address pool.
317
    pool = IPPool(net)
318 318
    try:
319
        address = pool.get_free_address()
320
    except IPPool.IPPoolExhausted:
319
        address = get_network_free_address(net)
320
    except EmptyPool:
321 321
        raise ServiceUnavailable('Network is full')
322
    pool.save()
323 322

  
324 323
    backend.connect_to_network(vm, net, address)
325 324
    return HttpResponse(status=202)
b/snf-cyclades-app/synnefo/api/util.py
63 63

  
64 64
from synnefo.lib.astakos import get_user
65 65
from synnefo.plankton.backend import ImageBackend
66
from synnefo.db.pools import IPPool
67 66
from synnefo.settings import MAX_CIDR_BLOCK
68 67

  
69 68

  
......
231 230
def get_network_free_address(network):
232 231
    """Reserve an IP address from the IP Pool of the network.
233 232

  
234
    Raises Network.DoesNotExist , IPPool.IPPoolExhausted
233
    Raises EmptyPool
235 234

  
236 235
    """
237 236

  
238
    # Get the Network object in exclusive mode in order to
239
    # safely (isolated) reserve an IP address
240
    network = Network.objects.select_for_update().get(id=network.id)
241
    pool = IPPool(network)
242
    address = pool.get_free_address()
237
    pool = network.get_pool()
238
    address = pool.get()
243 239
    pool.save()
244 240
    return address
245 241

  
b/snf-cyclades-app/synnefo/db/migrations/0054_auto__add_ippooltable__add_field_network_pool.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
        # Adding model 'IPPoolTable'
12
        db.create_table('db_ippooltable', (
13
            ('reserved_map', self.gf('django.db.models.fields.TextField')(default='')),
14
            ('base', self.gf('django.db.models.fields.CharField')(max_length=32, null=True)),
15
            ('offset', self.gf('django.db.models.fields.IntegerField')(null=True)),
16
            ('available_map', self.gf('django.db.models.fields.TextField')(default='')),
17
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
18
            ('size', self.gf('django.db.models.fields.IntegerField')()),
19
        ))
20
        db.send_create_signal('db', ['IPPoolTable'])
21

  
22
        # Adding field 'Network.pool'
23
        db.add_column('db_network', 'pool', self.gf('django.db.models.fields.related.OneToOneField')(related_name='network', unique=True, null=True, to=orm['db.IPPoolTable']), keep_default=False)
24
    
25
    
26
    def backwards(self, orm):
27
        
28
        # Deleting model 'IPPoolTable'
29
        db.delete_table('db_ippooltable')
30

  
31
        # Deleting field 'Network.pool'
32
        db.delete_column('db_network', 'pool_id')
33
    
34
    
35
    models = {
36
        'db.backend': {
37
            'Meta': {'object_name': 'Backend'},
38
            'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
39
            'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
40
            'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
41
            'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
42
            'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
43
            'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
44
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45
            'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}),
46
            'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
47
            'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
48
            'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
49
            'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
50
            'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
51
            'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}),
52
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
53
            'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
54
        },
55
        'db.backendnetwork': {
56
            'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'},
57
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}),
58
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
59
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
60
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
61
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
62
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
63
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
64
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
65
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
66
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
67
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}),
68
            'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}),
69
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
70
        },
71
        'db.bridgepooltable': {
72
            'Meta': {'object_name': 'BridgePoolTable'},
73
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
74
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
75
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
76
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
77
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
78
            'size': ('django.db.models.fields.IntegerField', [], {})
79
        },
80
        'db.flavor': {
81
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'},
82
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
83
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
84
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
85
            'disk_template': ('django.db.models.fields.CharField', [], {'default': "'plain'", 'max_length': '32'}),
86
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
87
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
88
        },
89
        'db.ippooltable': {
90
            'Meta': {'object_name': 'IPPoolTable'},
91
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
92
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
93
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
94
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
95
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
96
            'size': ('django.db.models.fields.IntegerField', [], {})
97
        },
98
        'db.macprefixpooltable': {
99
            'Meta': {'object_name': 'MacPrefixPoolTable'},
100
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
101
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
102
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
103
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
104
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
105
            'size': ('django.db.models.fields.IntegerField', [], {})
106
        },
107
        'db.network': {
108
            'Meta': {'object_name': 'Network'},
109
            'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}),
110
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
111
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
112
            'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
113
            'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
114
            'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
115
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
116
            'link': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
117
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
118
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
119
            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
120
            'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}),
121
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
122
            'reservations': ('django.db.models.fields.TextField', [], {'default': "''"}),
123
            'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}),
124
            'subnet': ('django.db.models.fields.CharField', [], {'default': "'10.0.0.0/24'", 'max_length': '32'}),
125
            'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
126
            'type': ('django.db.models.fields.CharField', [], {'default': "'PRIVATE_PHYSICAL_VLAN'", 'max_length': '50'}),
127
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
128
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'})
129
        },
130
        'db.networkinterface': {
131
            'Meta': {'object_name': 'NetworkInterface'},
132
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
133
            'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
134
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
135
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
136
            'index': ('django.db.models.fields.IntegerField', [], {}),
137
            'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
138
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
139
            'mac': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
140
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
141
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
142
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
143
        },
144
        'db.virtualmachine': {
145
            'Meta': {'object_name': 'VirtualMachine'},
146
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
147
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}),
148
            'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
149
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
150
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
151
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
152
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
153
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
154
            'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
155
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
156
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
157
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
158
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
159
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
160
            'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
161
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
162
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
163
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
164
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
165
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '100'})
166
        },
167
        'db.virtualmachinemetadata': {
168
            'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'},
169
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
170
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
171
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
172
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"})
173
        }
174
    }
175
    
176
    complete_apps = ['db']
b/snf-cyclades-app/synnefo/db/migrations/0055_data_migrate_ip_pools.py
1
# encoding: utf-8
2
import datetime
3
from south.db import db
4
from south.v2 import DataMigration
5
from django.db import models
6

  
7
from bitarray import bitarray
8
from base64 import b64encode, b64decode
9

  
10

  
11
class Migration(DataMigration):
12
    
13
    def forwards(self, orm):
14
        "Write your forwards methods here."
15
        for network in orm.Network.objects.all():
16
            if not network.reservations:
17
                continue
18
            old_res = bitarray()
19
            old_res.fromstring(b64decode(network.reservations))
20
            old_res.invert()
21

  
22
            available_map = b64encode(old_res.tostring())
23

  
24
            size = old_res.length()
25

  
26
            new_res = bitarray(size)
27
            new_res.setall(True)
28
            new_res[0] = False
29
            new_res[size-1] = False
30

  
31
            reserved_map = b64encode(new_res.tostring())
32

  
33
            pool = orm.IPPoolTable.objects.create(size=size,
34
                                                  available_map=available_map,
35
                                                  reserved_map=reserved_map)
36

  
37
            network.pool = pool
38
            network.save()
39
            
40
    
41
    
42
    def backwards(self, orm):
43
        "Write your backwards methods here."
44
        for network in orm.Network.objects.all():
45
            if not network.pool:
46
                network.reservations = ''
47
                network.save()
48
                continue
49
            pool = network.pool
50

  
51
            available = bitarray()
52
            available.fromstring(b64decode(pool.available_map))
53
            reserved = bitarray()
54
            reserved.fromstring(b64decode(pool.reserved_map))
55

  
56
            both = (available & reserved)
57
            both.invert()
58
            network.reservations = b64encode(both.tostring())
59
            network.save()
60
    
61
    models = {
62
        'db.backend': {
63
            'Meta': {'object_name': 'Backend'},
64
            'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
65
            'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
66
            'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
67
            'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
68
            'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
69
            'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
70
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
71
            'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}),
72
            'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
73
            'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
74
            'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
75
            'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
76
            'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
77
            'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}),
78
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
79
            'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
80
        },
81
        'db.backendnetwork': {
82
            'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'},
83
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}),
84
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
85
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
86
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
87
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
88
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
89
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
90
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
91
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
92
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
93
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}),
94
            'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}),
95
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
96
        },
97
        'db.bridgepooltable': {
98
            'Meta': {'object_name': 'BridgePoolTable'},
99
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
100
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
101
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
103
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
104
            'size': ('django.db.models.fields.IntegerField', [], {})
105
        },
106
        'db.flavor': {
107
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'},
108
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
109
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
110
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
111
            'disk_template': ('django.db.models.fields.CharField', [], {'default': "'plain'", 'max_length': '32'}),
112
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
114
        },
115
        'db.ippooltable': {
116
            'Meta': {'object_name': 'IPPoolTable'},
117
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
118
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
119
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
120
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
121
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
122
            'size': ('django.db.models.fields.IntegerField', [], {})
123
        },
124
        'db.macprefixpooltable': {
125
            'Meta': {'object_name': 'MacPrefixPoolTable'},
126
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
127
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
128
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
129
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
130
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
131
            'size': ('django.db.models.fields.IntegerField', [], {})
132
        },
133
        'db.network': {
134
            'Meta': {'object_name': 'Network'},
135
            'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}),
136
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
137
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
138
            'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
139
            'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
140
            'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
141
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
142
            'link': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
143
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
144
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
145
            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
146
            'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}),
147
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
148
            'reservations': ('django.db.models.fields.TextField', [], {'default': "''"}),
149
            'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}),
150
            'subnet': ('django.db.models.fields.CharField', [], {'default': "'10.0.0.0/24'", 'max_length': '32'}),
151
            'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
152
            'type': ('django.db.models.fields.CharField', [], {'default': "'PRIVATE_PHYSICAL_VLAN'", 'max_length': '50'}),
153
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
154
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'})
155
        },
156
        'db.networkinterface': {
157
            'Meta': {'object_name': 'NetworkInterface'},
158
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
159
            'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
160
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
161
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
162
            'index': ('django.db.models.fields.IntegerField', [], {}),
163
            'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
164
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
165
            'mac': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
166
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
167
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
168
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
169
        },
170
        'db.virtualmachine': {
171
            'Meta': {'object_name': 'VirtualMachine'},
172
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
173
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}),
174
            'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
175
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
176
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
177
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
178
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
179
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
180
            'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
181
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
182
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
183
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
184
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
185
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
186
            'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
187
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
188
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
189
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
190
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
191
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '100'})
192
        },
193
        'db.virtualmachinemetadata': {
194
            'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'},
195
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
196
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
197
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
198
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"})
199
        }
200
    }
201
    
202
    complete_apps = ['db']
b/snf-cyclades-app/synnefo/db/migrations/0056_auto__del_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
        # Deleting field 'Network.reservations'
12
        db.delete_column('db_network', 'reservations')
13
    
14
    
15
    def backwards(self, orm):
16
        
17
        # Adding field 'Network.reservations'
18
        db.add_column('db_network', 'reservations', self.gf('django.db.models.fields.TextField')(default=''), keep_default=False)
19
    
20
    
21
    models = {
22
        'db.backend': {
23
            'Meta': {'object_name': 'Backend'},
24
            'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
25
            'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
26
            'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
27
            'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
28
            'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
29
            'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
30
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
31
            'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}),
32
            'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
33
            'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
34
            'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
35
            'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
36
            'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
37
            'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}),
38
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
39
            'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
40
        },
41
        'db.backendnetwork': {
42
            'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'},
43
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}),
44
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
45
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
46
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
47
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
48
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
49
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
50
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
51
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
53
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}),
54
            'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}),
55
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
56
        },
57
        'db.bridgepooltable': {
58
            'Meta': {'object_name': 'BridgePoolTable'},
59
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
60
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
61
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
63
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
64
            'size': ('django.db.models.fields.IntegerField', [], {})
65
        },
66
        'db.flavor': {
67
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'},
68
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
69
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
70
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
71
            'disk_template': ('django.db.models.fields.CharField', [], {'default': "'plain'", 'max_length': '32'}),
72
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
73
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
74
        },
75
        'db.ippooltable': {
76
            'Meta': {'object_name': 'IPPoolTable'},
77
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
78
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
79
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
80
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
81
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
82
            'size': ('django.db.models.fields.IntegerField', [], {})
83
        },
84
        'db.macprefixpooltable': {
85
            'Meta': {'object_name': 'MacPrefixPoolTable'},
86
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
87
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
88
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
89
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
90
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
91
            'size': ('django.db.models.fields.IntegerField', [], {})
92
        },
93
        'db.network': {
94
            'Meta': {'object_name': 'Network'},
95
            'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}),
96
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
97
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
98
            'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
99
            'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
100
            'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
101
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102
            'link': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
103
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
104
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
105
            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
106
            'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}),
107
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
108
            'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}),
109
            'subnet': ('django.db.models.fields.CharField', [], {'default': "'10.0.0.0/24'", 'max_length': '32'}),
110
            'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
111
            'type': ('django.db.models.fields.CharField', [], {'default': "'PRIVATE_PHYSICAL_VLAN'", 'max_length': '50'}),
112
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
113
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'})
114
        },
115
        'db.networkinterface': {
116
            'Meta': {'object_name': 'NetworkInterface'},
117
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
118
            'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
119
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
120
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
121
            'index': ('django.db.models.fields.IntegerField', [], {}),
122
            'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
123
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
124
            'mac': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
125
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
126
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
127
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
128
        },
129
        'db.virtualmachine': {
130
            'Meta': {'object_name': 'VirtualMachine'},
131
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
132
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}),
133
            'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
134
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
135
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
136
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
137
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
138
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
139
            'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
140
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
141
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
142
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
143
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
144
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
145
            'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
146
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
147
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
148
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
149
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
150
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '100'})
151
        },
152
        'db.virtualmachinemetadata': {
153
            'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'},
154
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
155
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
156
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
157
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"})
158
        }
159
    }
160
    
161
    complete_apps = ['db']
b/snf-cyclades-app/synnefo/db/models.py
467 467
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
468 468
                              default=None)
469 469

  
470
    reservations = models.TextField(default='')
471

  
472
    ip_pool = None
470
    pool = models.OneToOneField('IPPoolTable', related_name='network',
471
                                null=True)
473 472

  
474 473
    objects = ForUpdateManager()
475 474

  
......
559 558
        for backend in backends:
560 559
            BackendNetwork.objects.create(backend=backend, network=self)
561 560

  
562
    @property
563
    def pool(self):
564
        if self.ip_pool:
565
            return self.ip_pool
566
        else:
567
            self.ip_pool = pools.IPPool(self)
568
            return self.ip_pool
561
    def get_pool(self):
562
        if not self.pool_id:
563
            self.pool = IPPoolTable.objects.create(available_map='',
564
                                                   reserved_map='',
565
                                                   size=0)
566
            self.save()
567
        return IPPoolTable.objects.select_for_update().get(id=self.pool_id).pool
569 568

  
570
    def reserve_address(self, address, pool=None):
571
        pool = pool or self.pool
569
    def reserve_address(self, address):
570
        pool = self.get_pool()
572 571
        pool.reserve(address)
573
        pool._update_network()
574
        self.save()
572
        pool.save()
575 573

  
576
    def release_address(self, address, pool=None):
577
        pool = pool or self.pool
578
        pool.release(address)
579
        pool._update_network()
580
        self.save()
574
    def release_address(self, address):
575
        pool = self.get_pool()
576
        pool.put(address)
577
        pool.save()
581 578

  
582 579

  
583 580
class BackendNetwork(models.Model):
......
712 709
class BridgePoolTable(PoolTable):
713 710
    manager = pools.BridgePool
714 711

  
712

  
715 713
class MacPrefixPoolTable(PoolTable):
716 714
    manager = pools.MacPrefixPool
715

  
716

  
717
class IPPoolTable(PoolTable):
718
    manager = pools.IPPool
b/snf-cyclades-app/synnefo/db/pools/__init__.py
67 67
        index = self.value_to_index(value)
68 68
        self._release(index, external)
69 69

  
70
    def reserve(self, value, external=True):
70
    def reserve(self, value, external=False):
71 71
        """Reserve a value."""
72 72
        index = self.value_to_index(value)
73 73
        self._reserve(index, external)
......
206 206
        return bin_[6] == '1' and bin_[7] == '0'
207 207

  
208 208

  
209
# class IPPool(PoolManager):
210
#     def __init__(self, network):
211
#         do_init = False if network.pool else True
212
#         self.net = ipaddr.IPNetwork(network.subnet)
213
#         network.size = self.net.numhosts
214
#         super(IPPool, self).__init__(network)
215
#         gateway = network.gateway
216
#         self.gateway = gateway and ipaddr.IPAddress(gateway) or None
217
#         if do_init:
218
#             self._reserve(0)
219
#             self.put(gateway)
220
#             self._reserve(self.size() - 1)
221
#
222
#     def value_to_index(self, value):
223
#         addr = ipaddr.IPAddress(value)
224
#         return int(addr) - int(self.net.network)
225
#
226
#     def index_to_value(self, index):
227
#         return str(self.net[index])
228

  
229
class IPPool(object):
230
    """IP pool class, based on a models.Network object
231

  
232
    Implementation of an IP address pool based on a models.Network
233
    object.
234

  
235
    """
236
    def __init__(self, network):
237
        self.net = network
238
        self.network = ipaddr.IPNetwork(self.net.subnet)
239

  
240
        gateway = self.net.gateway
209
class IPPool(PoolManager):
210
    def __init__(self, pool_table):
211
        do_init = False if pool_table.available_map else True
212
        network = pool_table.network
213
        self.net = ipaddr.IPNetwork(network.subnet)
214
        if not pool_table.size:
215
            pool_table.size = self.net.numhosts
216
        super(IPPool, self).__init__(pool_table)
217
        gateway = network.gateway
241 218
        self.gateway = gateway and ipaddr.IPAddress(gateway) or None
219
        if do_init:
220
            self._reserve(0)
221
            self.reserve(gateway)
222
            self._reserve(self.size() - 1)
242 223

  
243
        if self.net.reservations:
244
            self.reservations = bitarray()
245
            self.reservations.fromstring(b64decode(self.net.reservations))
246
        else:
247
            numhosts = self.network.numhosts
248
            self.reservations = bitarray(numhosts)
249
            self.reservations.setall(False)
250
            self.reservations[0] = True
251
            self.reservations[numhosts - 1] = True
252
            if self.gateway:
253
                self.reserve(self.gateway)
254

  
255
    def _contains(self, address):
256
        if address is None:
257
            return False
258
        addr = ipaddr.IPAddress(address)
259

  
260
        return (addr in self.network)
261

  
262
    def _address_index(self, address):
263
        """Convert IP address to bitarray index
264

  
265
        """
266
        if not self._contains(address):
267
            raise Exception("%s does not contain %s" %
268
                            (str(self.network), address))
269
        addr = ipaddr.IPAddress(address)
270

  
271
        return int(addr) - int(self.network.network)
272

  
273
    def _mark(self, address, value):
274
        index = self._address_index(address)
275
        self.reservations[index] = value
276

  
277
    def reserve(self, address):
278
        self._mark(address, True)
279

  
280
    def release(self, address):
281
        self._mark(address, False)
282

  
283
    def is_reserved(self, address):
284
        index = self._address_index(address)
285
        return self.reservations[index]
286

  
287
    def is_full(self):
288
            return self.reservations.all()
289

  
290
    def count_reserved(self):
291
        return self.reservations.count(True)
292

  
293
    def count_free(self):
294
        return self.reservations.count(False)
295

  
296
    def get_map(self):
297
        return self.reservations.to01().replace("1", "X").replace("0", ".")
298

  
299
    def get_free_address(self):
300
        """Get the first available address."""
301
        if self.is_full():
302
            raise IPPool.IPPoolExhausted("%s if full" % str(self.network))
303

  
304
        index = self.reservations.index(False)
305
        address = str(self.network[index])
306
        self.reserve(address)
307
        return address
308

  
309
    def save(self):
310
        """Update the Network model and save it to DB."""
311
        self._update_network()
312
        self.net.save()
313

  
314
    def _update_network(self):
315
        self.net.reservations = b64encode(self.reservations.tostring())
224
    def value_to_index(self, value):
225
        addr = ipaddr.IPAddress(value)
226
        return int(addr) - int(self.net.network)
316 227

  
317
    class IPPoolExhausted(Exception):
318
        pass
228
    def index_to_value(self, index):
229
        return str(self.net[index])
b/snf-cyclades-app/synnefo/logic/allocators/default_allocator.py
75 75
def has_free_ip(backend):
76 76
    """Find if Backend has any free public IP."""
77 77
    for network in backend_public_networks(backend):
78
        if not network.pool.is_full():
78
        if not network.get_pool().empty():
79 79
            return True
80 80
    return False
b/snf-cyclades-app/synnefo/logic/backend.py
41 41
from synnefo.db.models import (Backend, VirtualMachine, Network,
42 42
                               BackendNetwork, BACKEND_STATUSES)
43 43
from synnefo.logic import utils
44
from synnefo.db.pools import IPPool
44
from synnefo.db.pools import EmptyPool
45 45
from synnefo.api.faults import OverLimit
46 46
from synnefo.api.util import backend_public_networks, get_network_free_address
47 47
from synnefo.util.rapi import GanetiRapiClient
......
118 118
    Update the state of the VM in the DB accordingly.
119 119
    """
120 120

  
121
    # Release the ips of the old nics. Get back the networks as multiple
122
    # changes in the same network, must happen in the same Network object,
123
    # because transaction will be commited only on exit of the function.
124
    networks = release_instance_nics(vm)
121
    release_instance_nics(vm)
125 122

  
126 123
    new_nics = enumerate(nics)
127 124
    for i, new_nic in new_nics:
......
129 126
        n = str(network)
130 127
        pk = utils.id_from_network_name(n)
131 128

  
132
        # Get the cached Network or get it from DB
133
        if pk in networks:
134
            net = networks[pk]
135
        else:
136
            net = Network.objects.select_for_update().get(pk=pk)
129
        net = Network.objects.get(pk=pk)
137 130

  
138 131
        # Get the new nic info
139 132
        mac = new_nic.get('mac', '')
......
162 155

  
163 156

  
164 157
def release_instance_nics(vm):
165
    networks = {}
166

  
167 158
    for nic in vm.nics.all():
168
        pk = nic.network.pk
169
        # Get the cached Network or get it from DB
170
        if pk in networks:
171
            net = networks[pk]
172
        else:
173
            # Get the network object in exclusive mode in order
174
            # to guarantee consistency of the address pool
175
            net = Network.objects.select_for_update().get(pk=pk)
176
        if nic.ipv4:
177
            net.release_address(nic.ipv4)
159
        nic.network.release_address(nic.ipv4)
178 160
        nic.delete()
179 161

  
180
    return networks
181

  
182 162

  
183 163
@transaction.commit_on_success
184 164
def process_network_status(back_network, etime, jobid, opcode, status, logmsg):
......
367 347
        try:
368 348
            address = get_network_free_address(network)
369 349
            return (network, address)
370
        except IPPool.IPPoolExhausted:
350
        except EmptyPool:
371 351
            pass
372 352
    return (None, None)
373 353

  
b/snf-cyclades-app/synnefo/logic/management/commands/network-inspect.py
63 63
                  str(net.subnet), str(net.gateway), str(net.mac_prefix),
64 64
                  str(net.link), str(net.public),  str(net.dhcp),
65 65
                  str(net.type), str(net.deleted), str(net.action),
66
                  str(splitPoolMap(net.pool.get_map(), 64)))
66
                  str(splitPoolMap(net.get_pool().to_map(), 64)))
67 67

  
68 68
        self.stdout.write(sep)
69 69
        self.stdout.write('State of Network in DB\n')
b/snf-cyclades-app/synnefo/logic/management/commands/reconcile-networks.py
44 44
from django.db import transaction
45 45

  
46 46
from synnefo.db.models import Backend, Network, BackendNetwork
47
from synnefo.db.pools import IPPool
47 48
from synnefo.logic import reconciliation, backend, utils
48 49

  
49 50

  
......
88 89
        destroying = network.action == 'DESTROY'
89 90
        uses_pool = not (network.type == 'PUBLIC_ROUTED' and (not
90 91
                        PUBLIC_ROUTED_USE_POOL))
91
        ip_address_maps = []
92
        ip_available_maps = []
93
        ip_reserved_maps = []
92 94

  
93 95
        # Perform reconcilliation for each backend
94 96
        for b in backends:
......
164 166

  
165 167
            if uses_pool:
166 168
                # Reconcile IP Pools
167
                ip_map = ganeti_networks[b][net_id]['map']
168
                ip_address_maps.append(bitarray_from_o1(ip_map))
169

  
170
        if ip_address_maps and uses_pool:
171
            network_bitarray = reduce(lambda x, y: x | y, ip_address_maps)
172
            if not network.pool.reservations == network_bitarray:
173
                out.write('D: Unsynced pool of network %d\n' % net_id)
174
                out.write('\t DB:\t%s\n' % network.pool.reservations.to01())
175
                out.write('\t Ganeti:%s\n' % network_bitarray.to01())
169
                gnet = ganeti_networks[b][net_id]
170
                converter = IPPool(Foo(gnet['network']))
171
                a_map = bitarray_from_map(gnet['map'])
172
                reserved = gnet['external_reservations']
173
                r_map = a_map.copy()
174
                r_map.setall(True)
175
                for address in reserved.split(','):
176
                    index = converter.value_to_index(address)
177
                    a_map[index] = True
178
                    r_map[index] = False
179
                ip_available_maps.append(a_map)
180
                ip_reserved_maps.append(r_map)
181

  
182
        if uses_pool and (ip_available_maps or ip_reserved_maps):
183
            available_map = reduce(lambda x, y: x | y, ip_available_maps)
184
            available_map.invert()
185
            reserved_map = reduce(lambda x, y: x | y, ip_reserved_maps)
186

  
187
            pool = network.get_pool()
188
            un_available = pool.available != available_map
189
            un_reserved = pool.reserved != reserved_map
190
            if un_available or un_reserved:
191
                out.write("Detected unsynchronized pool for network %r:\n" %
192
                          network.id)
193
                if un_available:
194
                    out.write("Available:\n\tDB: %r\n\tGB: %r\n" %
195
                             (pool.available.to01(), available_map.to01()))
196
                    if fix:
197
                        pool.available = available_map
198
                if un_reserved:
199
                    out.write("Reserved:\n\tDB: %r\n\tGB: %r\n" %
200
                             (pool.reserved.to01(), reserved_map.to01()))
201
                    if fix:
202
                        pool.reserved = reserved_map
176 203
                if fix:
177
                    update_network_reservations(network, network_bitarray)
178
                    out.write('F: Synchronized network pools\n')
204
                    out.write("Synchronized pools for network %r.\n" % network.id)
205
            pool.save()
179 206

  
180 207
        # Detect conflicting IPs: Detect NIC's that have the same IP
181 208
        # in the same network.
......
206 233
        if len(orphans) > 0:
207 234
            out.write('D: Orphan Networks in backend %s:\n' % back_end.clustername)
208 235
            out.write('-  ' + '\n-  '.join([str(o) for o in orphans]) + '\n')
209
            client = back_end.client
210 236
            if fix:
211
                #XXX:Move this to backend
212
                for id in orphans:
213
                    out.write('Disconnecting and deleting network %d\n' % id)
214
                    network = utils.id_to_network_name(id)
215
                    for group in client.GetGroups():
216
                        client.DisconnectNetwork(network, group)
217
                        client.DeleteNetwork(network)
237
                for net_id in orphans:
238
                    out.write('Disconnecting and deleting network %d\n' % net_id)
239
                    network = Network.objects.get(id=net_id)
240
                    backend.delete_network(network, backends=[back_end])
218 241

  
219 242

  
220
def bitarray_from_o1(bitmap):
243
def bitarray_from_map(bitmap):
221 244
    return bitarray.bitarray(bitmap.replace("X", "1").replace(".", "0"))
222 245

  
223 246

  
......
225 248
def update_network_reservations(network, reservations):
226 249
    network.pool.reservations = reservations
227 250
    network.pool.save()
251

  
252

  
253
class Foo():
254
    def __init__(self, subnet):
255
        self.available_map = ''
256
        self.reserved_map = ''
257
        self.size = 0
258
        self.network = Foo.Foo1(subnet)
259

  
260
    class Foo1():
261
        def __init__(self, subnet):
262
            self.subnet = subnet
263
            self.gateway = None

Also available in: Unified diff