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