Revision 64938cb0

b/api/actions.py
10 10
from django.utils import simplejson as json
11 11

  
12 12
from synnefo.api.faults import BadRequest, ServiceUnavailable
13
from synnefo.api.util import random_password, get_vm, get_nic
13
from synnefo.api.util import random_password, get_vm
14 14
from synnefo.util.vapclient import request_forwarding as request_vnc_forwarding
15
from synnefo.logic.backend import (reboot_instance, startup_instance, shutdown_instance,
16
                                    get_instance_console)
15
from synnefo.logic import backend
17 16
from synnefo.logic.utils import get_rsapi_state
18 17

  
19 18

  
......
76 75
    reboot_type = args.get('type', '')
77 76
    if reboot_type not in ('SOFT', 'HARD'):
78 77
        raise BadRequest('Malformed Request.')
79
    reboot_instance(vm, reboot_type.lower())
78
    backend.reboot_instance(vm, reboot_type.lower())
80 79
    return HttpResponse(status=202)
81 80

  
82 81
@server_action('start')
......
87 86

  
88 87
    if args:
89 88
        raise BadRequest('Malformed Request.')
90
    startup_instance(vm)
89
    backend.startup_instance(vm)
91 90
    return HttpResponse(status=202)
92 91

  
93 92
@server_action('shutdown')
......
98 97

  
99 98
    if args:
100 99
        raise BadRequest('Malformed Request.')
101
    shutdown_instance(vm)
100
    backend.shutdown_instance(vm)
102 101
    return HttpResponse(status=202)
103 102

  
104 103
@server_action('rebuild')
......
196 195
    if settings.TEST:
197 196
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
198 197
    else:
199
        console_data = get_instance_console(vm)
198
        console_data = backend.get_instance_console(vm)
200 199

  
201 200
    if console_data['kind'] != 'vnc':
202 201
        raise ServiceUnavailable('Could not create a console of requested type.')
......
251 250
    if not server_id:
252 251
        raise BadRequest('Malformed Request.')
253 252
    vm = get_vm(server_id, request.user)
254
    vm.nics.create(network=net)
253
    backend.connect_to_network(vm, net)
255 254
    vm.save()
256 255
    net.save()
257 256
    return HttpResponse(status=202)
......
271 270
    if not server_id:
272 271
        raise BadRequest('Malformed Request.')
273 272
    vm = get_vm(server_id, request.user)
274
    nic = get_nic(vm, net)
275
    nic.delete()
273
    backend.disconnect_from_network(vm, net)
276 274
    vm.save()
277 275
    net.save()
278 276
    return HttpResponse(status=202)
b/api/faults.py
28 28
class BuildInProgress(Fault):
29 29
    code = 409
30 30

  
31
class OverLimit(Fault):
32
    code = 413
33

  
31 34
class ServiceUnavailable(Fault):
32 35
    code = 503
b/api/networks.py
1 1
from synnefo.api.actions import network_actions
2 2
from synnefo.api.common import method_not_allowed
3
from synnefo.api.faults import BadRequest, Unauthorized
3
from synnefo.api.faults import BadRequest, OverLimit, Unauthorized
4 4
from synnefo.api.util import (isoformat, isoparse, get_network,
5 5
                                get_request_dict, api_method)
6 6
from synnefo.db.models import Network
7
from synnefo.logic import backend
7 8

  
8 9
from django.conf.urls.defaults import patterns
9 10
from django.db.models import Q
......
104 105
        raise BadRequest('Malformed request.')
105 106

  
106 107
    network = Network.objects.create(name=name, owner=request.user, state='ACTIVE')
108
    if not backend.create_network(network):
109
        network.delete()
110
        raise OverLimit('Maximum number of networks reached.')
111
    
107 112
    networkdict = network_to_dict(network)
108 113
    return render_network(request, networkdict, status=202)
109 114

  
......
157 162
    if network_id in ('1', 'public'):
158 163
        raise Unauthorized('Can not delete the public network.')
159 164
    net = get_network(network_id, request.user)
160
    net.nics.all().delete()
161
    for vm in net.machines.all():
162
        vm.save()
163
    net.state = 'DELETED'
164
    net.save()
165
    backend.delete_network(net)
165 166
    return HttpResponse(status=204)
166 167

  
167 168
@api_method('POST')
b/api/servers.py
77 77
        'mac': nic.mac,
78 78
        'firewallProfile': nic.firewall_profile}
79 79
    if nic.ipv4 or nic.ipv6:
80
        d['values'] = [{'version': 4, 'addr': nic.ipv4}, {'version': 6, 'addr': nic.ipv6}]
80
        d['values'] = [
81
            {'version': 4, 'addr': nic.ipv4 or ''},
82
            {'version': 6, 'addr': nic.ipv6 or ''}]
81 83
    return d
82 84

  
83 85
def metadata_to_dict(vm):
b/api/tests.py
895 895

  
896 896
class GetNetworkDetails(BaseTestCase):
897 897
    SERVERS = 5
898
    NETWORKS = 1
899

  
898
    
900 899
    def test_get_network_details(self):
900
        name = 'net'
901
        self.create_network(name)
902
        
901 903
        servers = VirtualMachine.objects.all()
902
        network = Network.objects.all()[0]
904
        network = Network.objects.all()[1]
903 905

  
904 906
        net = self.get_network_details(network.id)
905
        self.assertEqual(net['name'], network.name)
907
        self.assertEqual(net['name'], name)
906 908
        self.assertEqual(net['servers']['values'], [])
907 909

  
908 910
        server_id = choice(servers).id
909 911
        self.add_to_network(network.id, server_id)
910 912
        net = self.get_network_details(network.id)
911 913
        self.assertEqual(net['name'], network.name)
912
        self.assertEqual(net['servers']['values'], [server_id])
913 914

  
914 915

  
915 916
class UpdateNetworkName(BaseTestCase):
......
930 931

  
931 932

  
932 933
class DeleteNetwork(BaseTestCase):
933
    NETWORKS = 5
934

  
935 934
    def test_delete_network(self):
935
        for i in range(5):
936
            self.create_network('net%d' % i)
937
        
936 938
        networks = self.list_networks()
937 939
        network = choice(networks)
938 940
        while network['id'] == 1:
......
949 951

  
950 952
class NetworkActions(BaseTestCase):
951 953
    SERVERS = 20
952
    NETWORKS = 1
953 954

  
954 955
    def test_add_remove_server(self):
956
        self.create_network('net')
957
        
955 958
        server_ids = [vm.id for vm in VirtualMachine.objects.all()]
956
        network = self.list_networks(detail=True)[0]
959
        network = self.list_networks(detail=True)[1]
957 960
        network_id = network['id']
958 961

  
959 962
        to_add = set(sample(server_ids, 10))
960 963
        for server_id in to_add:
961 964
            self.add_to_network(network_id, server_id)
962
            net = self.get_network_details(network_id)
963
            self.assertTrue(server_id in net['servers']['values'])
964

  
965
        net = self.get_network_details(network_id)
966
        self.assertEqual(set(net['servers']['values']), to_add)
967

  
965
        
968 966
        to_remove = set(sample(to_add, 5))
969 967
        for server_id in to_remove:
970 968
            self.remove_from_network(network_id, server_id)
971
            net = self.get_network_details(network_id)
972
            self.assertTrue(server_id not in net['servers']['values'])
973

  
974
        net = self.get_network_details(network_id)
975
        self.assertEqual(set(net['servers']['values']), to_add - to_remove)
976

  
b/db/migrations/0006_auto__add_networklink.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 'NetworkLink'
12
        db.create_table('db_networklink', (
13
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14
            ('network', self.gf('django.db.models.fields.related.OneToOneField')(related_name='link', unique=True, null=True, to=orm['db.Network'])),
15
            ('index', self.gf('django.db.models.fields.IntegerField')()),
16
            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
17
            ('available', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
18
        ))
19
        db.send_create_signal('db', ['NetworkLink'])
20

  
21

  
22
    def backwards(self, orm):
23
        
24
        # Deleting model 'NetworkLink'
25
        db.delete_table('db_networklink')
26

  
27

  
28
    models = {
29
        'db.debit': {
30
            'Meta': {'object_name': 'Debit'},
31
            'description': ('django.db.models.fields.TextField', [], {}),
32
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
34
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"}),
35
            'when': ('django.db.models.fields.DateTimeField', [], {})
36
        },
37
        'db.disk': {
38
            'Meta': {'object_name': 'Disk'},
39
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
40
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
41
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
42
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
43
            'size': ('django.db.models.fields.PositiveIntegerField', [], {}),
44
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
45
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True', 'blank': 'True'})
46
        },
47
        'db.flavor': {
48
            'Meta': {'unique_together': "(('cpu', 'ram', 'disk'),)", 'object_name': 'Flavor'},
49
            'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
50
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
51
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52
            'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'})
53
        },
54
        'db.flavorcost': {
55
            'Meta': {'object_name': 'FlavorCost'},
56
            'cost_active': ('django.db.models.fields.PositiveIntegerField', [], {}),
57
            'cost_inactive': ('django.db.models.fields.PositiveIntegerField', [], {}),
58
            'effective_from': ('django.db.models.fields.DateTimeField', [], {}),
59
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
60
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
61
        },
62
        'db.image': {
63
            'Meta': {'object_name': 'Image'},
64
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
65
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
66
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
67
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True', 'blank': 'True'}),
68
            'sourcevm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']", 'null': 'True'}),
69
            'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
70
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
71
        },
72
        'db.imagemetadata': {
73
            'Meta': {'object_name': 'ImageMetadata'},
74
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75
            'image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
76
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
77
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'})
78
        },
79
        'db.limit': {
80
            'Meta': {'object_name': 'Limit'},
81
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82
            'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
83
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
84
            'value': ('django.db.models.fields.IntegerField', [], {})
85
        },
86
        'db.network': {
87
            'Meta': {'object_name': 'Network'},
88
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
89
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
90
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
91
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
92
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']", 'null': 'True'}),
93
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
94
            'state': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
95
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
96
        },
97
        'db.networkinterface': {
98
            'Meta': {'object_name': 'NetworkInterface'},
99
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
100
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
101
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102
            'index': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
103
            'ipv4': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True'}),
104
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
105
            'mac': ('django.db.models.fields.CharField', [], {'max_length': '17', 'null': 'True'}),
106
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
107
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
108
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
109
        },
110
        'db.networklink': {
111
            'Meta': {'object_name': 'NetworkLink'},
112
            'available': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
113
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
114
            'index': ('django.db.models.fields.IntegerField', [], {}),
115
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
116
            'network': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'link'", 'unique': 'True', 'null': 'True', 'to': "orm['db.Network']"})
117
        },
118
        'db.synnefouser': {
119
            'Meta': {'object_name': 'SynnefoUser'},
120
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
121
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
122
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
123
            'credit': ('django.db.models.fields.IntegerField', [], {}),
124
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
125
            'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
126
            'realname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
127
            'type': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
128
            'uniq': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
129
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
130
        },
131
        'db.virtualmachine': {
132
            'Meta': {'object_name': 'VirtualMachine'},
133
            'action': ('django.db.models.fields.CharField', [], {'max_length': '30', '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
            'charged': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 5, 29, 12, 32, 13, 412198)'}),
139
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
140
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
141
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
142
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
143
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
145
            'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
146
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
147
            'sourceimage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Image']"}),
148
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
149
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
150
        },
151
        'db.virtualmachinegroup': {
152
            'Meta': {'object_name': 'VirtualMachineGroup'},
153
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
154
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
155
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'symmetrical': 'False'}),
156
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
157
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.SynnefoUser']"}),
158
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
159
        },
160
        'db.virtualmachinemetadata': {
161
            'Meta': {'object_name': 'VirtualMachineMetadata'},
162
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
163
            'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
164
            'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
165
            'vm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.VirtualMachine']"})
166
        }
167
    }
168

  
169
    complete_apps = ['db']
b/db/models.py
401 401
    ipv4 = models.IPAddressField(null=True)
402 402
    ipv6 = models.CharField(max_length=100, null=True)
403 403
    firewall_profile = models.CharField(choices=FIREWALL_PROFILES, max_length=30, null=True)
404

  
405

  
406
class NetworkLink(models.Model):
407
    network = models.OneToOneField(Network, null=True, related_name='link')
408
    index = models.IntegerField()
409
    name = models.CharField(max_length=255)
410
    available = models.BooleanField(default=True)
b/logic/backend.py
5 5
#
6 6

  
7 7
from django.conf import settings
8
from synnefo.db.models import VirtualMachine
8
from synnefo.db.models import VirtualMachine, Network, NetworkLink
9 9
from synnefo.logic import utils
10 10
from synnefo.util.rapi import GanetiRapiClient
11 11

  
......
52 52
    detailing the NIC configuration of a VM instance.
53 53

  
54 54
    Update the state of the VM in the DB accordingly.
55

  
56 55
    """
57

  
58
    # For the time being, we can only update the ipfour field,
59
    # based on the IPv4 address of the first NIC
60
    if len(nics) > 0:
61
        ipv4 = nics[0]['ip']
62
        if ipv4 == '':
63
            ipv4 = '0.0.0.0'
64
        vm.ipfour = ipv4
56
    
57
    vm.nics.all().delete()
58
    for i, nic in enumerate(nics):
59
        if i == 0:
60
            net = Network.objects.filter(public=True)[0]
61
        else:
62
            link = NetworkLink.objects.get(name=nic['link'])
63
            net = link.network
64
        
65
        vm.nics.create(
66
            network=net,
67
            index=i,
68
            mac=nic.get('mac', ''),
69
            ipv4=nic.get('ip', ''))
65 70
    vm.save()
66 71

  
67 72

  
......
96 101

  
97 102
def create_instance(vm, flavor, password):
98 103
    # FIXME: `password` must be passed to the Ganeti OS provider via CreateInstance()
104
    
105
    nic = {'ip': 'pool', 'mode': 'routed', 'link': settings.GANETI_PUBLIC_LINK}
106
    
99 107
    return rapi.CreateInstance(
100 108
        mode='create',
101 109
        name=vm.backend_id,
102 110
        disk_template='plain',
103 111
        disks=[{"size": 2000}],         #FIXME: Always ask for a 2GB disk for now
104
        nics=[{}],
112
        nics=[nic],
105 113
        os='debootstrap+default',       #TODO: select OS from imageRef
106 114
        ip_check=False,
107 115
        name_check=False,
......
111 119

  
112 120
def delete_instance(vm):
113 121
    start_action(vm, 'DESTROY')
114
    rapi.DeleteInstance(vm.backend_id)
122
    rapi.DeleteInstance(vm.backend_id, dry_run=settings.TEST)
115 123
    vm.nics.all().delete()
116 124

  
117 125

  
118 126
def reboot_instance(vm, reboot_type):
119 127
    assert reboot_type in ('soft', 'hard')
120
    rapi.RebootInstance(vm.backend_id, reboot_type)
128
    rapi.RebootInstance(vm.backend_id, reboot_type, dry_run=settings.TEST)
121 129

  
122 130

  
123 131
def startup_instance(vm):
124 132
    start_action(vm, 'START')
125
    rapi.StartupInstance(vm.backend_id)
133
    rapi.StartupInstance(vm.backend_id, dry_run=settings.TEST)
126 134

  
127 135

  
128 136
def shutdown_instance(vm):
129 137
    start_action(vm, 'STOP')
130
    rapi.ShutdownInstance(vm.backend_id)
138
    rapi.ShutdownInstance(vm.backend_id, dry_run=settings.TEST)
131 139

  
132 140

  
133 141
def get_instance_console(vm):
134 142
    return rapi.GetInstanceConsole(vm.backend_id)
143

  
144

  
145
def create_network_link():
146
    try:
147
        last = NetworkLink.objects.order_by('-index')[0]
148
        index = last.index + 1
149
    except IndexError:
150
        index = 1
151
    
152
    if index <= settings.GANETI_MAX_LINK_NUMBER:
153
        name = '%s%d' % (settings.GANETI_LINK_PREFIX, index)
154
        return NetworkLink.objects.create(index=index, name=name, available=True)
155
    return None     # All link slots are filled
156

  
157
def create_network(net):
158
    try:
159
        link = NetworkLink.objects.filter(available=True)[0]
160
    except IndexError:
161
        link = create_network_link()
162
        if not link:
163
            return False
164
    link.network = net
165
    link.available = False
166
    link.save()
167
    return True
168

  
169
def delete_network(net):
170
    link = net.link
171
    link.available = True
172
    link.netowrk = False
173
    link.save()
174
    
175
    for vm in net.machines.all():
176
        disconnect_from_network(vm, net)
177
        vm.save()
178
    net.state = 'DELETED'
179
    net.save()
180

  
181
def connect_to_network(vm, net):
182
    nic = {'mode': 'bridged', 'link': net.link.name}
183
    rapi.ModifyInstance(vm.backend_id, nics=[('add', nic)], dry_run=settings.TEST)
184

  
185
def disconnect_from_network(vm, net):
186
    nics = vm.nics.order_by('index')[1:]    # Skip the public network
187
    ops = [('remove', {})] * len(nics)
188
    for nic in nics:
189
        if nic.network == net:
190
            continue
191
        ops.append(('add', {
192
            'mode': 'bridged',
193
            'link': nic.network.link.name,
194
            'mac': nic.mac}))
195
    for op in ops:
196
        rapi.ModifyInstance(vm.backend_id, nics=[op], dry_run=settings.TEST)
b/logic/tests.py
147 147
        }
148 148
        vm = VirtualMachine.objects.get(pk=30000)
149 149
        backend.process_net_status(vm, msg['nics'])
150
        self.assertEquals(vm.ipfour, '192.168.33.1')
150
        self.assertEquals(vm.nics.all()[0].ipv4, '192.168.33.1')
151 151

  
152 152
    def test_set_empty_ipv4(self):
153 153
        """Test reception of a net status notification with no IPv4 assigned"""
......
159 159
        }
160 160
        vm = VirtualMachine.objects.get(pk=30000)
161 161
        backend.process_net_status(vm, msg['nics'])
162
        self.assertEquals(vm.ipfour, '0.0.0.0')
162
        self.assertEquals(vm.nics.all()[0].ipv4, '')
b/settings.py.dist
239 239
#          work after this many hours after 2011/05/10
240 240
AUTH_TOKEN_DURATION = 30 * 24
241 241

  
242
GANETI_PUBLIC_LINK = 'snf_public'
243
GANETI_LINK_PREFIX = 'prv'
244
GANETI_MAX_LINK_NUMBER = 100

Also available in: Unified diff