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