Revision 2e0916f2

b/snf-cyclades-app/synnefo/db/migrations/0073_auto__add_field_virtualmachine_router.py
1
# -*- coding: 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

  
8
class Migration(SchemaMigration):
9

  
10
    def forwards(self, orm):
11
        # Adding field 'VirtualMachine.router'
12
        db.add_column('db_virtualmachine', 'router',
13
                      self.gf('django.db.models.fields.BooleanField')(default=False),
14
                      keep_default=False)
15

  
16

  
17
    def backwards(self, orm):
18
        # Deleting field 'VirtualMachine.router'
19
        db.delete_column('db_virtualmachine', 'router')
20

  
21

  
22
    models = {
23
        'db.backend': {
24
            'Meta': {'ordering': "['clustername']", 'object_name': 'Backend'},
25
            'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
26
            'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
27
            'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
28
            'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
29
            'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
30
            'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
31
            'hypervisor': ('django.db.models.fields.CharField', [], {'default': "'kvm'", 'max_length': '32'}),
32
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33
            'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}),
34
            'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
35
            'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
36
            'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
37
            'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
38
            'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
39
            'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}),
40
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
41
            'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
42
        },
43
        'db.backendnetwork': {
44
            'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'},
45
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}),
46
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
47
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
48
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
49
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
50
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
51
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
52
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
53
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
54
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
55
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}),
56
            'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}),
57
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
58
        },
59
        'db.bridgepooltable': {
60
            'Meta': {'object_name': 'BridgePoolTable'},
61
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
62
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
63
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
64
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
65
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
66
            'size': ('django.db.models.fields.IntegerField', [], {})
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'}),
72
            'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
73
            'disk_template': ('django.db.models.fields.CharField', [], {'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.floatingip': {
78
            'Meta': {'object_name': 'FloatingIP'},
79
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
80
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
81
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82
            'ipv4': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15', 'db_index': 'True'}),
83
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'null': 'True', 'to': "orm['db.VirtualMachine']"}),
84
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'to': "orm['db.Network']"}),
85
            'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'floating_ips'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
86
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
87
        },
88
        'db.ippooltable': {
89
            'Meta': {'object_name': 'IPPoolTable'},
90
            'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
91
            'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
92
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93
            'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
94
            'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}),
95
            'size': ('django.db.models.fields.IntegerField', [], {})
96
        },
97
        'db.macprefixpooltable': {
98
            'Meta': {'object_name': 'MacPrefixPoolTable'},
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.network': {
107
            'Meta': {'object_name': 'Network'},
108
            'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}),
109
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
110
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
111
            'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
112
            'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
113
            'flavor': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
114
            'floating_ip_pool': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
115
            'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
116
            'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
117
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118
            'link': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
119
            'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
120
            'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}),
121
            'mode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True'}),
122
            'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
123
            'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}),
124
            'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
125
            'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'network'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
126
            'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}),
127
            'subnet': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
128
            'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
129
            'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
130
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
131
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'db_index': 'True'})
132
        },
133
        'db.networkinterface': {
134
            'Meta': {'object_name': 'NetworkInterface'},
135
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
136
            'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
137
            'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
138
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139
            'index': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
140
            'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}),
141
            'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
142
            'mac': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}),
143
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}),
144
            'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}),
145
            'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '32'}),
146
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
147
        },
148
        'db.quotaholderserial': {
149
            'Meta': {'ordering': "['serial']", 'object_name': 'QuotaHolderSerial'},
150
            'accept': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
151
            'pending': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
152
            'resolved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
153
            'serial': ('django.db.models.fields.BigIntegerField', [], {'primary_key': 'True', 'db_index': 'True'})
154
        },
155
        'db.virtualmachine': {
156
            'Meta': {'object_name': 'VirtualMachine'},
157
            'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '30', 'null': 'True'}),
158
            'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}),
159
            'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
160
            'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
161
            'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
162
            'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}),
163
            'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
164
            'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}),
165
            'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
166
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
167
            'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
168
            'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}),
169
            'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
170
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
171
            'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
172
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
173
            'operstate': ('django.db.models.fields.CharField', [], {'default': "'BUILD'", 'max_length': '30'}),
174
            'router': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
175
            'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machine'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}),
176
            'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
177
            'task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
178
            'task_job_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
179
            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
180
            'userid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'})
181
        },
182
        'db.virtualmachinediagnostic': {
183
            'Meta': {'ordering': "['-created']", 'object_name': 'VirtualMachineDiagnostic'},
184
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
185
            'details': ('django.db.models.fields.TextField', [], {'null': 'True'}),
186
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
187
            'level': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
188
            'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'diagnostics'", 'to': "orm['db.VirtualMachine']"}),
189
            'message': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
190
            'source': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
191
            'source_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'})
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/models.py
359 359
    task = models.CharField(max_length=64, null=True)
360 360
    task_job_id = models.BigIntegerField(null=True)
361 361

  
362
    #is this virtualmachine a router
363
    router = models.BooleanField('router', default=False)
364

  
362 365
    objects = ForUpdateManager()
363 366

  
364 367
    def get_client(self):
b/snf-cyclades-app/synnefo/db/models_factory.py
101 101
    suspended = False
102 102
    operstate = factory.Sequence(round_seq_first(FACTORY_FOR.OPER_STATES))
103 103

  
104

  
105 104
class DeletedVirtualMachine(VirtualMachineFactory):
106 105
    deleted = True
107 106

  
b/snf-cyclades-app/synnefo/neutron/models.py
124 124
                             default='PENDING')
125 125
    machines = models.ManyToManyField(VirtualMachine,
126 126
                                      through='NetworkInterface',
127
                                      related_name='neutron_machines')
127
                                      related_name='neutron_networks')
128 128
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
129 129
                              default=None)
130 130
    drained = models.BooleanField("Drained", default=False, null=False)
......
134 134
    #serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
135 135
    #                           null=True)
136 136

  
137
    public = models.BooleanField('public', default=True)
137 138
    objects = ForUpdateManager()
138 139

  
139
    def __unicode__(self):
140
        return "<Network: %s>" % str(self.id)
141

  
142
    @property
143
    def backend_id(self):
144
        """Return the backend id by prepending backend-prefix."""
145
        if not self.id:
146
            raise Network.InvalidBackendIdError("self.id is None")
147
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
148

  
149
    @property
150
    def backend_tag(self):
151
        """Return the network tag to be used in backend
152

  
153
        """
154
        if self.tags:
155
            return self.tags.split(',')
156
        else:
157
            return []
158

  
159
    def create_backend_network(self, backend=None):
160
        """Create corresponding BackendNetwork entries."""
161

  
162
        backends = [backend] if backend else\
163
            Backend.objects.filter(offline=False)
164
        for backend in backends:
165
            backend_exists =\
166
                BackendNetwork.objects.filter(backend=backend, network=self)\
167
                                      .exists()
168
            if not backend_exists:
169
                BackendNetwork.objects.create(backend=backend, network=self)
170

  
171
    def get_pool(self, with_lock=True):
172
        if not self.pool_id:
173
            self.pool = IPPoolTable.objects.create(available_map='',
174
                                                   reserved_map='',
175
                                                   size=0)
176
            self.save()
177
        objects = IPPoolTable.objects
178
        if with_lock:
179
            objects = objects.select_for_update()
180
        return objects.get(id=self.pool_id).pool
181

  
182
    def reserve_address(self, address):
183
        pool = self.get_pool()
184
        pool.reserve(address)
185
        pool.save()
186

  
187
    def release_address(self, address):
188
        pool = self.get_pool()
189
        pool.put(address)
190
        pool.save()
191

  
192
    class InvalidBackendIdError(Exception):
193
        def __init__(self, value):
194
            self.value = value
195

  
196
        def __str__(self):
197
            return repr(self.value)
198

  
199
    class InvalidBackendMsgError(Exception):
200
        def __init__(self, opcode, status):
201
            self.opcode = opcode
202
            self.status = status
203

  
204
        def __str__(self):
205
            return repr('<opcode: %s, status: %s>'
206
                        % (self.opcode, self.status))
207

  
208
    class InvalidActionError(Exception):
209
        def __init__(self, action):
210
            self._action = action
211

  
212
        def __str__(self):
213
            return repr(str(self._action))
214

  
215 140

  
216 141
class Subnet(models.Model):
217 142
    SUBNET_NAME_LENGTH = 128
......
243 168
        ("BUILDING", "Building"),
244 169
    )
245 170

  
246
    name = models.CharField('Network Name', max_length=128)
171
    name = models.CharField('nic name', max_length=128)
247 172
    machine = models.ForeignKey(VirtualMachine, related_name='neutron_nics')
248 173
    network = models.ForeignKey(Network, related_name='neutron_nics')
174
    subnet = models.ForeignKey(Subnet, related_names='neutron_nics')
249 175
    created = models.DateTimeField(auto_now_add=True)
250 176
    updated = models.DateTimeField(auto_now=True)
251 177
    index = models.IntegerField(null=True)
......
271 197
            return network.floating_ips.filter(machine=self.machine,
272 198
                                               ipv4=self.ipv4,
273 199
                                               deleted=False).exists()
200

  
201

  
202

  
b/snf-cyclades-app/synnefo/neutron/network_views.py
60 60

  
61 61
    if request.serialization == 'xml':
62 62
        data = render_to_string('list_networks.xml', {
63
            'networks': networks})
63
            "networks": networks})
64 64
    else:
65 65
        data = json.dumps({'networks': networks})
66 66

  
......
212 212
    updatable = set(["name", "admin_state_up"])
213 213
    try:
214 214
        new_vals = info["network"]
215
    except Keyerror:
215
    except KeyError:
216 216
        raise api.faults.BadRequest("Malformed request")
217 217

  
218 218
    for key, val in new_vals.iteritems():
b/snf-cyclades-app/synnefo/neutron/router_views.py
1
from django.http import HttpResponse
2
from django.utils import simplejson as json
3
from django.db import transaction
4
from django.db.models import Q
5
from synnefo.db.pools import EmptyPool
6
from synnefo.db.utils import validate_mac
7
from django.conf import settings
8
from snf_django.lib import api
9
from snf_django.lib.api import utils
10
from synnefo.logic import backend
11
from django.template.loader import render_to_string
12
from synnefo.api import util
13

  
14
from synnefo.db.models import VirtualMachine
15
import models
16
from logging import getLogger
17

  
18
import network_views as nv
19
#from synnefo.logic import servers
20

  
21
log = getLogger(__name__)
22

  
23

  
24
def demux(request):
25
    if request.method == 'GET':
26
        #return HttpResponse("list routers")
27
        return list_routers(request)
28
    elif request.method == 'POST':
29
        #return create_router(request)
30
        return HttpResponse("create router")
31
    else:
32
        return api.api_method_not_allowed(request)
33

  
34

  
35
def router_demux(request, offset):
36
    if request.method == 'GET':
37
        #return HttpResponse("get single router")
38
        return get_router(request,  offset)
39
    elif request.method == 'DELETE':
40
        #return HttpResponse("delete router")
41
        return delete_router(request, offset)
42
    elif request.method == 'PUT':
43
        #return HttpResponse("put router")
44
        return update_router(request,offset)
45
    else:
46
        return api.api_method_not_allowed(request)
47

  
48

  
49
def rinterface_demux(request, offset1, offset2):
50
    if request.method == 'PUT':
51
        if offset2 == "add_router_interface":
52
            #return HttpResponse("add interface")
53
            return add_interface(request, offset2)
54
        elif offset2 == "remove_router_interface":
55
            #return HttpResponse("remove interface")
56
            return remove_interface(request, offset22)
57
    else:
58
        return api.api_method_not_allowed(request)
59

  
60

  
61
@api.api_method(http_method='GET', user_required=True, logger=log)
62
def list_routers(request):
63
    log.debug('list_routers')
64

  
65
    user_routers = VirtualMachine.objects.filter(userid=request.user_uniq, router=True)
66

  
67
    user_routers = utils.filter_modified_since(request, objects=user_routers)
68

  
69
    routers = [router_to_dict(router)
70
               for router in user_routers.order_by('id')]
71

  
72
    if request.serialization == 'xml':
73
        data = render_to_string('list_routers.xml', {
74
            'routers': routers})
75
    else:
76
        data = json.dumps({"routers": routers})
77

  
78
    return HttpResponse(data, status=200)
79

  
80
'''
81
@api.api_method(http_method='POST', user_required=True, logger=log)
82
@transaction.commit_manually
83
def create_router(request):
84
    user_id = request.user_uniq
85
    req = utils.get_request_dict(request)
86

  
87
    try:
88
        server = req['router']
89
        name = server['name']
90
    except (KeyError, AssertionError):
91
        raise api.faults.BadRequest("Malformed request")
92
    metadata = server.get('metadata', {})
93
    assert isinstance(metadata, dict)
94
    image_id = server['imageRef']
95
    flavor_id = server['flavorRef']
96
    personality = server.get('personality', [])
97
    assert isinstance(personality, list)
98
    private_networks = server.get("networks", [])
99
    assert isinstance(private_networks, list)
100
    floating_ips = server.get("floating_ips", [])
101
    assert isinstance(floating_ips, list)
102
    # Verify that personalities are well-formed
103
    util.verify_personality(personality)
104
    # Get image information
105
    image = util.get_image_dict(image_id, user_id)
106
    # Get flavor (ensure it is active)
107
    flavor = util.get_flavor(flavor_id, include_deleted=False)
108
    # Generate password
109
    password = util.random_password()
110
    vm = servers.create(user_id,
111
                        name,
112
                        #password,
113
                        #flavor,
114
                        #image,
115
                        #metadata=metadata,
116
                        #personality=personality,
117
                        #private_networks=private_networks,
118
                        #floating_ips=floating_ips
119
                        )
120

  
121
    server = router_to_dict(vm, detail=True)
122
    server['status'] = 'BUILD'
123
    server['adminPass'] = password
124

  
125
    response = render_router(request, server, status=202)
126

  
127
    return response
128

  
129

  
130
'''
131
@api.api_method(http_method='GET', user_required=True, logger=log)
132
def get_router(request, router_id):
133
    log.debug('get_router_details %s', router_id)
134
    router = util.get_vm(router_id, request.user_uniq)
135

  
136
    router_dict = router_to_dict(router)
137
    return render_router(request, router_dict)
138

  
139

  
140
@api.api_method(http_method='DELETE', user_required=True, logger=log)
141
@transaction.commit_on_success
142
def delete_router(request, router_id):
143
    log.debug('delete router  %s', router_id)
144
    router = util.get_vm(router_id, request.user_uniq)
145

  
146
    if router.neutron_networks.filter(public=False):
147
        return HttpResponse("There are internal interfaces on the router", status=409)
148

  
149
    #servers.destroy(router)
150

  
151
    router.deleted = True
152
    router.save()
153
    return HttpResponse(status=204)
154

  
155

  
156
@api.api_method(http_method='PUT', user_required=True, logger=log)
157
def update_router(request, router_id):
158
    log.debug('update router %s', router_id)
159
    router = util.get_vm(router_id, request.user_uniq)
160

  
161
    req = utils.get_request_dict(request)
162
    try:
163
        server = req['router']
164
    except (KeyError, AssertionError):
165
        raise api.faults.BadRequest("Malformed request")
166

  
167
    try:
168
        name = server['name']
169
        router.name = name
170
    except KeyError:
171
        pass
172

  
173
    try:
174
        net = server['external_gateway_info']['network_id']
175

  
176
        #find the new network
177
        new_net = nv.get_network_fromdb(net, request.user_uniq)
178

  
179
        #disconnect from the old net
180
        external_net = router.neutron_networks.filter(public=True)
181
        if external_net:
182
            #servers.disconnect(router, external_net[0])
183
            mock_disconnect(router, external_net[0])
184

  
185
        #connect to the new net
186
        #servers.connect(router, new_net)
187
        mock_connect(router, new_net)
188
    except KeyError:
189
        pass
190

  
191
    router.save()
192
    routerdict = router_to_dict(router)
193
    return render_router(request, routerdict, 200)
194

  
195

  
196
@api.api_method(http_method='PUT', user_required=True, logger=log)
197
def add_interface(request, router_id):
198
    log.debug('add interface to router %s', router_id)
199
    router = util.get_vm(router_id, request.user_uniq)
200

  
201
    router_info = utils.get_request_dict(request)
202

  
203
    subnet = False
204
    try:
205
        subnet_id = router_info["subnet_id"]
206
        subnet = True
207
        subnet_obj = subnet_views.get_subnet_fromdb(subnet_id,
208
                                        request.user_unique)
209
    except KeyError:
210
        pass
211

  
212
    try:
213
        port_id = router_info['port_id']
214
        if subnet:
215
            raise api.faults.BadRequest("Both subnet and port provided")
216
        try:
217
            nic_obj = models.NetworkInteface.objects.get(id=port_id)
218
        except (ValueError, models.NetworkInteraface.DoesNotExist):
219
            raise api.faults.ItemNotFound('Port not found')
220
        if not nic_obj.ipv4:
221
            raise api.faults.BadRequest("No ip-address")
222
        if nic_obj.machine:
223
            return HttpResponse(status=409)
224
    except KeyError:
225
        if not subnet:
226
            raise api.faults.BadRequest("Malformed request")
227

  
228
    if subnet:
229
        #create nic with the subnet and add to the vm
230
        nic = models.NetworkInteface(machine=router,
231
                                    subnet=subnet_obj,
232
                                    network=subnet_obj.network
233
                                    ipv4=subnet.gateway
234
                                    )
235
        nic.save()
236
        port_id = nic.id
237
    else:
238
        #connect the nic
239
        nic_obj.machine = router
240
        nic_obj.save()
241
        subnet_id = nic.subnet.id
242

  
243
    res = {"port_id":port_db.id,
244
            "subnet_id":subnet_db.id
245
          }
246

  
247
    data = json.dumps(res)
248
    return HttpResponse(data, status=200)
249

  
250
@api.api_method(http_method='PUT', user_required=True, logger=log)
251
def remove_interface(request, router_id):
252

  
253
    log.debug('remove interface from router %s', router_id)
254
    router = util.get_vm(router_id, request.user_uniq)
255

  
256
    info = utils.get_request_dict(request)
257

  
258
    subnet = False
259
    try:
260
        subnet_id = info["subnet_id"]
261
        subnet = True
262
        subnet_db = subnet_views.get_subnet_fromdb(subnet_id,
263
                                request.user_unique)
264
    except KeyError:
265
        pass
266

  
267
    port = False
268
    try:
269
        port_id = info['port_id']
270
        port = True
271
        try:
272
            nic_obj = models.NetworkInteface.objects.get(id=port_id)
273
        except (ValueError, models.NetworkInteraface.DoesNotExist):
274
            raise api.faults.ItemNotFound('Port not found')
275
    except KeyError:
276
        pass
277

  
278
    if port and subnet:
279
        #validate port-subnet combination else return 409
280
        #subnet must be the first of the port
281
        if not port_db is subnet_db:
282
            return HttpResponse(status=409)
283

  
284
    if subnet:
285
        #get the port
286
        if not port_db:
287
            port_db = subnet_db.neutron_nics.get(machine=router)
288
    elif port:
289
        #get the subnet
290
        if not subnet_db:
291
            subnet_db = port_db.subnet
292
    else:
293
        raise api.faults.BadRequest("Malformed request")
294

  
295
    res = {"id":str(router.id),
296
            "tenant_id":request.user_uniq,
297
            "port_id":port_db.id,
298
            "subnet_id":subnet_db.id
299
          }
300

  
301
    port_db.delete()  # need to add backend stuff
302
    data = json.dumps(res)
303
    return HttpResponse(data, status=200)
304

  
305

  
306
# util functions
307

  
308

  
309
def router_to_dict(router):
310
    d = {'id': str(router.id), 'name': router.name}
311
    d['user_id'] = router.userid
312
    d['tenant_id'] = router.userid
313
    d['admin_state_up'] = True
314
    external_net = router.neutron_networks.filter(public=True)
315
    if external_net:
316
        external_net = external_net[0]
317
        d['external_gateway_info'] = {'network_id': external_net.id}
318
    else:
319
        d['external_gateway_info'] = None
320
    return d
321

  
322

  
323
def render_router(request, routerdict, status=200):
324
    if request.serialization == 'xml':
325
        data = render_to_string('router.xml', {'router': routerdict})
326
    else:
327
        data = json.dumps({'router': routerdict})
328
    return HttpResponse(data, status=status)
329

  
330

  
331
# mock functions
332

  
333
def mock_disconnect(router, net):
334
    nic = models.NetworkInterface.objects.get(network=net, machine=router)
335
    nic.delete()
336

  
337
def mock_connect(router, net):
338
    nic = models.NetworkInterface(network=net, machine=router)
339
    nic.save()
b/snf-cyclades-app/synnefo/neutron/subnet_views.py
162 162
    raise api.faults.BadRequest("Deletion of a subnet is not supported")
163 163

  
164 164

  
165

  
165 166
@api.api_method(http_method='PUT', user_required=True, logger=log)
166 167
def update_subnet(request, sub_id):
167 168
    '''Update info of a subnet'''
168 169

  
169 170

  
171
# util functions
172

  
173

  
170 174
def subnet_to_dict(subnet):
171 175
    '''Returns a dictionary containing the info of a subnet'''
172 176
    # FIX ME, allocation pools
......
184 188
    if network.subnet_set.filter(ipversion=version):
185 189
        raise api.faults.BadRequest("Only one subnet of IPv4/IPv6 per "
186 190
                                    "network is allowed")
191

  
192
def get_subnet_fromdb(subnet_id, user_id, for_update=False):
193
    """
194
    Return a Subnet instance or raise ItemNotFound.
195
    This is the same as util.get_network
196
    """
197
    try:
198
        subnet_id = int(subnet_id)
199
        objects = Subnet.objects
200
        if for_update:
201
            objects = objects.select_for_update()
202
        return objects.get(userid=user_id, id=subnet_id)
203
    except (ValueError, Subnet.DoesNotExist):
204
        raise api.faults.ItemNotFound('Network not found.')
205

  
b/snf-cyclades-app/synnefo/neutron/tests/api.py
10 10
NEUTRON_URL = get_service_path(cyclades_services, "neutron", "v2.0")
11 11
NETWORKS_URL = join_urls(NEUTRON_URL, "networks/")
12 12
SUBNETS_URL = join_urls(NEUTRON_URL, "subnets/")
13

  
13
ROUTERS_URL = join_urls(NEUTRON_URL, "routers/")
14 14

  
15 15
class NetworkTest(BaseAPITest):
16 16

  
......
18 18
        response = self.get(NETWORKS_URL)
19 19
        self.assertSuccess(response)
20 20
        networks = json.loads(response.content)
21
        self.assertEqual(networks, {'networks': []})
21
        self.assertEqual(networks, {"networks": []})
22 22

  
23 23
    def test_create_network(self):
24 24
        request = {
......
99 99
        url = join_urls(NETWORKS_URL, str(test_net.id))
100 100
        request = {
101 101
            "network": {
102
                "wronng_field": "new_name"}
102
                "wrong_field": "new_name"}
103 103
        }
104 104
        response = self.put(url, params=json.dumps(request),
105 105
                            user=test_net.userid)
......
112 112
        self.assertEqual(response.status_code, 400)
113 113

  
114 114

  
115
class RouterTest(BaseAPITest):
116
    def test_list_empty_routers(self):
117
        response = self.get(ROUTERS_URL)
118
        self.assertSuccess(response)
119
        routers = json.loads(response.content)
120
        self.assertEqual(routers, {"routers": []})
121

  
122
    def test_create_router(self):
123
        pass
124

  
125
    def test_get_router(self):
126
        router = dbmf.VirtualMachineFactory.create(router=True)
127
        net = mf.NetworkFactory.create(public=True)
128
        nic = mf.NetworkInterfaceFactory.create(network=net, machine=router)
129
        response = self.get(ROUTERS_URL, user=router.userid)
130
        self.assertSuccess(response)
131

  
132
    def test_delete_router(self):
133
        router = dbmf.VirtualMachineFactory.create(router=True)
134
        url = join_urls(ROUTERS_URL, str(router.id))
135
        response = self.delete(url, user=router.userid)
136
        print response.content
137
        self.assertEqual(response.status_code, 204)
138

  
139
    def test_delete_router_with_private_net(self):
140
        router = dbmf.VirtualMachineFactory.create(router=True)
141
        net = mf.NetworkFactory.create(public=False)
142
        nic = mf.NetworkInterfaceFactory.create(network=net, machine=router)
143
        url = join_urls(ROUTERS_URL, str(router.id))
144
        response = self.delete(url, user=router.userid)
145
        print response.content
146
        self.assertEqual(response.status_code, 409)
147

  
148
    def test_update_router(self):
149
        router = dbmf.VirtualMachineFactory.create(router=True)
150
        net1 = mf.NetworkFactory.create(public=True)
151
        net2 = mf.NetworkFactory.create(public=True)
152
        nic = mf.NetworkInterfaceFactory.create(network=net1, machine=router)
153
        request = {"router":{
154
                        "name": "new_name",
155
                        "external_gateway_info":{
156
                           "network_id": net2.id
157
                           }
158
                    }
159
            }
160
        url = join_urls(ROUTERS_URL, str(router.id))
161
        response = self.put(url, params=json.dumps(request), user=router.userid)
162
        info = json.loads(response.content)
163
        self.assertEqual(info['router']['external_gateway_info']['network_id'], net2.id)
164
        self.assertEqual(info['router']['name'], "new_name")
165

  
166
    def test_remove_interface(self):
167
        url = join_urls(join_urls(ROUTERS_URL,"123"), "remove_router_interface")
168
        response = self.put(url, params="")
169
        print response.content
170

  
171
    def test_add_interface(self):
172
        url = join_urls(join_urls(ROUTERS_URL,"123"), "add_router_interface")
173
        request =  {"port_id": "a2f1f29d-571b-4533-907f-5803ab96ead1","subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"}
174
        print json.dumps(request)
175
        response = self.put(url, params=json.dumps(request))
176
        print response.content
177

  
115 178
class SubnetTest(BaseAPITest):
116 179
    def test_list_subnets(self):
117 180
        '''Test list subnets without data'''
b/snf-cyclades-app/synnefo/neutron/urls.py
45 45
    (r'^subnets(?:/|.json|.xml)?$', 'subnet_views.demux'),
46 46
    (r'^subnets/(\w+)(?:/|.json|.xml)?$', 'subnet_views.subnet_demux'),
47 47
    (r'^ports(?:/|.json|.xml)?$', 'port_views.demux'),
48
    (r'^ports/(\w+)(?:/|.json|.xml)?$', 'port_views.port_demux'))
48
    (r'^ports/(\w+)(?:/|.json|.xml)?$', 'port_views.port_demux'),
49
    (r'^routers(?:/|.json|.xml)?$', 'router_views.demux'),
50
    (r'^routers/(\w+)(?:/|.json|.xml)?$', 'router_views.router_demux'),
51
    (r'^routers/(\w+)/(remove_router_interface|add_router_interface)$', 'router_views.rinterface_demux'))
49 52

  
50 53
urlpatterns = patterns(
51 54
    '',

Also available in: Unified diff