Revision 99af08a4
b/snf-cyclades-app/synnefo/api/management/commands/network-create.py | ||
---|---|---|
39 | 39 |
|
40 | 40 |
from synnefo import quotas |
41 | 41 |
from synnefo.db.models import Network |
42 |
from synnefo.db.utils import validate_mac, InvalidMacAddress |
|
42 | 43 |
from synnefo.logic.backend import create_network |
43 | 44 |
from synnefo.api.util import values_from_flavor |
44 | 45 |
|
... | ... | |
174 | 175 |
mac_prefix = mac_prefix or fmac_prefix |
175 | 176 |
tags = tags or ftags |
176 | 177 |
|
178 |
try: |
|
179 |
validate_mac(mac_prefix + "0:00:00:00") |
|
180 |
except InvalidMacAddress: |
|
181 |
raise CommandError("Invalid MAC prefix '%s'" % mac_prefix) |
|
177 | 182 |
subnet, gateway, subnet6, gateway6 = validate_network_info(options) |
178 | 183 |
|
179 | 184 |
if not link or not mode: |
... | ... | |
193 | 198 |
"link": link, |
194 | 199 |
"mac_prefix": mac_prefix, |
195 | 200 |
"tags": tags, |
196 |
"state": "PENDING"}
|
|
201 |
"state": "ACTIVE"}
|
|
197 | 202 |
|
198 | 203 |
if dry_run: |
199 | 204 |
self.stdout.write("Creating network:\n") |
... | ... | |
204 | 209 |
if userid: |
205 | 210 |
quotas.issue_and_accept_commission(network) |
206 | 211 |
|
207 |
if public:
|
|
212 |
if backend_id:
|
|
208 | 213 |
# Create BackendNetwork only to the specified Backend |
209 | 214 |
network.create_backend_network(backend) |
210 |
create_network(network, backends=[backend]) |
|
211 |
else: |
|
212 |
# Create BackendNetwork entries for all Backends |
|
213 |
network.create_backend_network() |
|
214 |
create_network(network) |
|
215 |
create_network(network=network, backend=backend, connect=True) |
b/snf-cyclades-app/synnefo/api/management/commands/network-remove.py | ||
---|---|---|
55 | 55 |
|
56 | 56 |
network.action = 'DESTROY' |
57 | 57 |
network.save() |
58 |
|
|
58 | 59 |
quotas.issue_and_accept_commission(network, delete=True) |
59 |
delete_network(network) |
|
60 |
|
|
61 |
for bnet in network.backend_networks.exclude(operstate="DELETED"): |
|
62 |
delete_network(network, bnet.backend) |
|
60 | 63 |
|
61 | 64 |
self.stdout.write('Successfully removed network.\n') |
b/snf-cyclades-app/synnefo/api/networks.py | ||
---|---|---|
46 | 46 |
from synnefo.api.actions import network_actions |
47 | 47 |
from synnefo import quotas |
48 | 48 |
from synnefo.db.models import Network |
49 |
from synnefo.db.utils import validate_mac |
|
49 | 50 |
from synnefo.db.pools import EmptyPool |
50 | 51 |
from synnefo.logic import backend |
51 | 52 |
|
... | ... | |
177 | 178 |
elif flavor not in Network.FLAVORS.keys(): |
178 | 179 |
raise faults.BadRequest("Invalid network type '%s'" % flavor) |
179 | 180 |
elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS: |
180 |
raise faults.Forbidden("Can not create network of type '%s'" % flavor) |
|
181 |
raise faults.Forbidden("Can not create network of type '%s'" % |
|
182 |
flavor) |
|
181 | 183 |
|
182 | 184 |
public = d.get("public", False) |
183 | 185 |
if public: |
... | ... | |
195 | 197 |
|
196 | 198 |
try: |
197 | 199 |
mode, link, mac_prefix, tags = util.values_from_flavor(flavor) |
200 |
validate_mac(mac_prefix + "0:00:00:00") |
|
198 | 201 |
network = Network.objects.create( |
199 | 202 |
name=name, |
200 | 203 |
userid=user_id, |
... | ... | |
209 | 212 |
mac_prefix=mac_prefix, |
210 | 213 |
tags=tags, |
211 | 214 |
action='CREATE', |
212 |
state='PENDING')
|
|
215 |
state='ACTIVE')
|
|
213 | 216 |
except EmptyPool: |
214 | 217 |
log.error("Failed to allocate resources for network of type: %s", |
215 | 218 |
flavor) |
216 |
raise faults.ServiceUnavailable("Failed to allocate network resources") |
|
219 |
raise faults.ServiceUnavailable("Failed to allocate network" |
|
220 |
" resources") |
|
217 | 221 |
|
218 |
# Create BackendNetwork entries for each Backend |
|
219 |
network.create_backend_network() |
|
220 | 222 |
# Issue commission to Quotaholder and accept it since at the end of |
221 | 223 |
# this transaction the Network object will be created in the DB. |
222 | 224 |
# Note: the following call does a commit! |
... | ... | |
227 | 229 |
else: |
228 | 230 |
transaction.commit() |
229 | 231 |
|
230 |
# Create the network in the actual backends |
|
231 |
backend.create_network(network) |
|
232 |
|
|
233 | 232 |
networkdict = network_to_dict(network, request.user_uniq) |
234 | 233 |
response = render_network(request, networkdict, status=202) |
235 | 234 |
|
... | ... | |
307 | 306 |
net.action = 'DESTROY' |
308 | 307 |
net.save() |
309 | 308 |
|
310 |
backend.delete_network(net) |
|
309 |
backend_networks = net.backend_networks.exclude(operstate="DELETED") |
|
310 |
for bnet in backend_networks: |
|
311 |
backend.delete_network(net, bnet.backend) |
|
311 | 312 |
return HttpResponse(status=204) |
312 | 313 |
|
313 | 314 |
|
b/snf-cyclades-app/synnefo/db/models.py | ||
---|---|---|
417 | 417 |
|
418 | 418 |
class Network(models.Model): |
419 | 419 |
OPER_STATES = ( |
420 |
('PENDING', 'Pending'), |
|
420 |
('PENDING', 'Pending'), # Unused because of lazy networks
|
|
421 | 421 |
('ACTIVE', 'Active'), |
422 | 422 |
('DELETED', 'Deleted'), |
423 | 423 |
('ERROR', 'Error') |
... | ... | |
649 | 649 |
mac_prefix) |
650 | 650 |
self.mac_prefix = mac_prefix |
651 | 651 |
|
652 |
def __unicode__(self): |
|
653 |
return '<%s@%s>' % (self.network, self.backend) |
|
654 |
|
|
652 | 655 |
|
653 | 656 |
class NetworkInterface(models.Model): |
654 | 657 |
FIREWALL_PROFILES = ( |
... | ... | |
727 | 730 |
backend = obj |
728 | 731 |
|
729 | 732 |
if backend.offline: |
733 |
log.warning("Trying to connect with offline backend: %s", backend) |
|
730 | 734 |
raise faults.ServiceUnavailable |
731 | 735 |
|
732 | 736 |
b = backend |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
234 | 234 |
|
235 | 235 |
|
236 | 236 |
def update_network_state(network): |
237 |
old_state = network.state
|
|
237 |
"""Update the state of a Network based on BackendNetwork states.
|
|
238 | 238 |
|
239 |
backend_states = [s.operstate for s in |
|
240 |
network.backend_networks.filter(backend__offline=False)] |
|
241 |
if not backend_states: |
|
242 |
network.state = 'PENDING' |
|
243 |
network.save() |
|
239 |
Update the state of a Network based on the operstate of the networks in the |
|
240 |
backends that network exists. |
|
241 |
|
|
242 |
The state of the network is: |
|
243 |
* ACTIVE: If it is 'ACTIVE' in at least one backend. |
|
244 |
* DELETED: If it is is 'DELETED' in all backends that have been created. |
|
245 |
|
|
246 |
This function also releases the resources (MAC prefix or Bridge) and the |
|
247 |
quotas for the network. |
|
248 |
|
|
249 |
""" |
|
250 |
if network.deleted: |
|
251 |
# Network has already been deleted. Just assert that state is also |
|
252 |
# DELETED |
|
253 |
if not network.state == "DELETED": |
|
254 |
network.state = "DELETED" |
|
255 |
network.save() |
|
244 | 256 |
return |
245 | 257 |
|
246 |
all_equal = len(set(backend_states)) <= 1 |
|
247 |
network.state = all_equal and backend_states[0] or 'PENDING' |
|
258 |
backend_states = [s.operstate for s in network.backend_networks.all()] |
|
259 |
if not backend_states: |
|
260 |
if network.state != "ACTIVE": |
|
261 |
network.state = "ACTIVE" |
|
262 |
network.save() |
|
263 |
return |
|
264 |
|
|
265 |
# Network is deleted when all BackendNetworks go to "DELETED" operstate |
|
266 |
deleted = reduce(lambda x, y: x == y, backend_states, 'DELETED') |
|
267 |
|
|
248 | 268 |
# Release the resources on the deletion of the Network |
249 |
if old_state != 'DELETED' and network.state == 'DELETED':
|
|
269 |
if deleted:
|
|
250 | 270 |
log.info("Network %r deleted. Releasing link %r mac_prefix %r", |
251 | 271 |
network.id, network.mac_prefix, network.link) |
252 | 272 |
network.deleted = True |
273 |
network.state = "DELETED" |
|
253 | 274 |
if network.mac_prefix: |
254 | 275 |
if network.FLAVORS[network.flavor]["mac_prefix"] == "pool": |
255 | 276 |
release_resource(res_type="mac_prefix", |
... | ... | |
457 | 478 |
return client.GetInstanceInfo(vm.backend_vm_id) |
458 | 479 |
|
459 | 480 |
|
460 |
def create_network(network, backends=None, connect=True): |
|
461 |
"""Create and connect a network.""" |
|
462 |
if not backends: |
|
463 |
backends = Backend.objects.exclude(offline=True) |
|
481 |
def create_network(network, backend, connect=True): |
|
482 |
"""Create a network in a Ganeti backend""" |
|
483 |
log.debug("Creating network %s in backend %s", network, backend) |
|
464 | 484 |
|
465 |
log.debug("Creating network %s in backends %s", network, backends)
|
|
485 |
job_id = _create_network(network, backend)
|
|
466 | 486 |
|
467 |
for backend in backends: |
|
468 |
create_jobID = _create_network(network, backend) |
|
469 |
if connect: |
|
470 |
connect_network(network, backend, create_jobID) |
|
487 |
if connect: |
|
488 |
job_ids = connect_network(network, backend, depends=[job_id]) |
|
489 |
return job_ids |
|
490 |
else: |
|
491 |
return [job_id] |
|
471 | 492 |
|
472 | 493 |
|
473 | 494 |
def _create_network(network, backend): |
... | ... | |
503 | 524 |
tags=tags) |
504 | 525 |
|
505 | 526 |
|
506 |
def connect_network(network, backend, depend_job=None, group=None):
|
|
527 |
def connect_network(network, backend, depends=[], group=None):
|
|
507 | 528 |
"""Connect a network to nodegroups.""" |
508 | 529 |
log.debug("Connecting network %s to backend %s", network, backend) |
509 | 530 |
|
... | ... | |
512 | 533 |
else: |
513 | 534 |
conflicts_check = False |
514 | 535 |
|
515 |
depend_jobs = [depend_job] if depend_job else []
|
|
536 |
depends = [[job, ["success", "error", "canceled"]] for job in depends]
|
|
516 | 537 |
with pooled_rapi_client(backend) as client: |
517 |
if group: |
|
518 |
client.ConnectNetwork(network.backend_id, group, network.mode, |
|
519 |
network.link, conflicts_check, depend_jobs) |
|
520 |
else: |
|
521 |
for group in client.GetGroups(): |
|
522 |
client.ConnectNetwork(network.backend_id, group, network.mode, |
|
523 |
network.link, conflicts_check, |
|
524 |
depend_jobs) |
|
538 |
groups = [group] if group is not None else client.GetGroups() |
|
539 |
job_ids = [] |
|
540 |
for group in groups: |
|
541 |
job_id = client.ConnectNetwork(network.backend_id, group, |
|
542 |
network.mode, network.link, |
|
543 |
conflicts_check, |
|
544 |
depends=depends) |
|
545 |
job_ids.append(job_id) |
|
546 |
return job_ids |
|
525 | 547 |
|
526 | 548 |
|
527 |
def delete_network(network, backends=None, disconnect=True): |
|
528 |
if not backends: |
|
529 |
backends = Backend.objects.exclude(offline=True) |
|
549 |
def delete_network(network, backend, disconnect=True): |
|
550 |
log.debug("Deleting network %s from backend %s", network, backend) |
|
530 | 551 |
|
531 |
log.debug("Deleting network %s from backends %s", network, backends) |
|
552 |
depends = [] |
|
553 |
if disconnect: |
|
554 |
depends = disconnect_network(network, backend) |
|
555 |
_delete_network(network, backend, depends=depends) |
|
532 | 556 |
|
533 |
for backend in backends: |
|
534 |
disconnect_jobIDs = [] |
|
535 |
if disconnect: |
|
536 |
disconnect_jobIDs = disconnect_network(network, backend) |
|
537 |
_delete_network(network, backend, disconnect_jobIDs) |
|
538 | 557 |
|
539 |
|
|
540 |
def _delete_network(network, backend, depend_jobs=[]):
|
|
558 |
def _delete_network(network, backend, depends=[]): |
|
559 |
depends = [[job, ["success", "error", "canceled"]] for job in depends]
|
|
541 | 560 |
with pooled_rapi_client(backend) as client: |
542 |
return client.DeleteNetwork(network.backend_id, depend_jobs)
|
|
561 |
return client.DeleteNetwork(network.backend_id, depends) |
|
543 | 562 |
|
544 | 563 |
|
545 | 564 |
def disconnect_network(network, backend, group=None): |
546 | 565 |
log.debug("Disconnecting network %s to backend %s", network, backend) |
547 | 566 |
|
548 | 567 |
with pooled_rapi_client(backend) as client: |
549 |
if group: |
|
550 |
return [client.DisconnectNetwork(network.backend_id, group)] |
|
551 |
else: |
|
552 |
jobs = [] |
|
553 |
for group in client.GetGroups(): |
|
554 |
job = client.DisconnectNetwork(network.backend_id, group) |
|
555 |
jobs.append(job) |
|
556 |
return jobs |
|
568 |
groups = [group] if group is not None else client.GetGroups() |
|
569 |
job_ids = [] |
|
570 |
for group in groups: |
|
571 |
job_id = client.DisconnectNetwork(network.backend_id, group) |
|
572 |
job_ids.append(job_id) |
|
573 |
return job_ids |
|
557 | 574 |
|
558 | 575 |
|
559 | 576 |
def connect_to_network(vm, network, address=None): |
577 |
backend = vm.backend |
|
578 |
bnet, created = BackendNetwork.objects.get_or_create(backend=backend, |
|
579 |
network=network) |
|
580 |
depend_jobs = [] |
|
581 |
if bnet.operstate != "ACTIVE": |
|
582 |
depend_jobs = create_network(network, backend, connect=True) |
|
583 |
|
|
584 |
depends = [[job, ["success", "error", "canceled"]] for job in depend_jobs] |
|
585 |
|
|
560 | 586 |
nic = {'ip': address, 'network': network.backend_id} |
561 | 587 |
|
562 | 588 |
log.debug("Connecting vm %s to network %s(%s)", vm, network, address) |
... | ... | |
564 | 590 |
with pooled_rapi_client(vm) as client: |
565 | 591 |
return client.ModifyInstance(vm.backend_vm_id, nics=[('add', nic)], |
566 | 592 |
hotplug=settings.GANETI_USE_HOTPLUG, |
593 |
depends=depends, |
|
567 | 594 |
dry_run=settings.TEST) |
568 | 595 |
|
569 | 596 |
|
b/snf-cyclades-app/synnefo/logic/callbacks.py | ||
---|---|---|
105 | 105 |
network_id = utils.id_from_network_name(msg["network"]) |
106 | 106 |
network = Network.objects.select_for_update().get(id=network_id) |
107 | 107 |
backend = Backend.objects.get(clustername=msg['cluster']) |
108 |
backend_network = BackendNetwork.objects.get(network=network, |
|
109 |
backend=backend) |
|
110 |
func(backend_network, msg) |
|
108 |
bnet, new = BackendNetwork.objects.get_or_create(network=network, |
|
109 |
backend=backend) |
|
110 |
if new: |
|
111 |
log.info("Created missing BackendNetwork %s", bnet) |
|
112 |
func(bnet, msg) |
|
111 | 113 |
except Network.InvalidBackendIdError: |
112 | 114 |
log.debug("Ignoring msg for unknown network %s.", msg['network']) |
113 | 115 |
except Network.DoesNotExist: |
b/snf-cyclades-app/synnefo/logic/management/commands/reconcile-networks.py | ||
---|---|---|
108 | 108 |
for bend in backends: |
109 | 109 |
bnet = get_backend_network(network, bend) |
110 | 110 |
if not bnet: |
111 |
# CASE-1: Paritioned network |
|
112 |
if not network.public: |
|
113 |
bnet = reconcile_parted_network(network, bend) |
|
114 |
if not fix: |
|
115 |
continue |
|
116 |
else: |
|
111 |
try: |
|
112 |
gnet = ganeti_networks[bend][network.id] |
|
113 |
if not network.public and network.action != "DESTROY": |
|
114 |
# Network exists in Ganeti backend, but not in DB. |
|
115 |
reconcile_parted_network(network, bend) |
|
116 |
except KeyError: |
|
117 |
# Network does not exist either in DB not in Ganeti. |
|
118 |
# Nothing to reconcile.. |
|
117 | 119 |
continue |
118 | 120 |
|
119 | 121 |
try: |
... | ... | |
201 | 203 |
write("D: Missing Ganeti network %s in backend %s\n" % |
202 | 204 |
(network, backend)) |
203 | 205 |
if fix: |
204 |
backend_mod.create_network(network, [backend])
|
|
206 |
backend_mod.create_network(network, backend)
|
|
205 | 207 |
write("F: Issued OP_NETWORK_CONNECT\n") |
206 | 208 |
|
207 | 209 |
|
... | ... | |
213 | 215 |
for group in hanging_groups: |
214 | 216 |
write('F: Connecting network %s to nodegroup %s\n' |
215 | 217 |
% (network, group)) |
216 |
backend_mod.connect_network(network, backend, group=group) |
|
218 |
backend_mod.connect_network(network, backend, depends=[], |
|
219 |
group=group) |
|
217 | 220 |
|
218 | 221 |
|
219 | 222 |
def reconcile_unsynced_network(network, backend, backend_network): |
... | ... | |
278 | 281 |
try: |
279 | 282 |
network = Network.objects.get(id=net_id) |
280 | 283 |
backend_mod.delete_network(network, |
281 |
backends=[back_end])
|
|
284 |
backend=back_end)
|
|
282 | 285 |
except Network.DoesNotExist: |
283 | 286 |
write("Not entry for network %s in DB !!\n" % net_id) |
284 | 287 |
|
b/snf-cyclades-app/synnefo/logic/rapi.py | ||
---|---|---|
1707 | 1707 |
} |
1708 | 1708 |
|
1709 | 1709 |
if depends: |
1710 |
body['depends'] = [] |
|
1711 |
for d in depends: |
|
1712 |
body['depends'].append([d, ["success"]]) |
|
1710 |
body['depends'] = depends |
|
1713 | 1711 |
|
1714 | 1712 |
query = [] |
1715 | 1713 |
_AppendDryRunIf(query, dry_run) |
... | ... | |
1728 | 1726 |
} |
1729 | 1727 |
|
1730 | 1728 |
if depends: |
1731 |
body['depends'] = [] |
|
1732 |
for d in depends: |
|
1733 |
body['depends'].append([d, ["success"]]) |
|
1729 |
body['depends'] = depends |
|
1734 | 1730 |
|
1735 | 1731 |
query = [] |
1736 | 1732 |
_AppendDryRunIf(query, dry_run) |
... | ... | |
1769 | 1765 |
""" |
1770 | 1766 |
body = {} |
1771 | 1767 |
if depends: |
1772 |
body['depends'] = [] |
|
1773 |
for d in depends: |
|
1774 |
body['depends'].append([d, ["success"]]) |
|
1768 |
body['depends'] = depends |
|
1775 | 1769 |
|
1776 | 1770 |
query = [] |
1777 | 1771 |
_AppendDryRunIf(query, dry_run) |
Also available in: Unified diff