Revision d2036274

b/snf-cyclades-app/synnefo/api/management/commands/network-modify.py
35 35

  
36 36
from django.core.management.base import BaseCommand, CommandError
37 37

  
38
from synnefo.db.models import (Backend, BackendNetwork)
38
from synnefo.db.models import Backend
39 39
from synnefo.management.common import (get_network, get_backend)
40 40
from snf_django.management.utils import parse_bool
41 41
from synnefo.logic import networks, backend as backend_mod
......
127 127
        if floating_ip_pool is not None:
128 128
            floating_ip_pool = parse_bool(floating_ip_pool)
129 129
            if floating_ip_pool is False and network.floating_ip_pool is True:
130
                if network.ips.filter(deleted=False, floating_ip=True).exists():
130
                if network.ips.filter(deleted=False, floating_ip=True)\
131
                              .exists():
131 132
                    msg = ("Cannot make network a non floating IP pool."
132 133
                           " There are still reserved floating IPs.")
133 134
                    raise CommandError(msg)
......
137 138
                              (network, floating_ip_pool))
138 139
            if floating_ip_pool is True:
139 140
                for backend in Backend.objects.filter(offline=False):
140
                    try:
141
                        bnet = network.backend_networks.get(backend=backend)
142
                    except BackendNetwork.DoesNotExist:
143
                        bnet = network.create_backend_network(backend=backend)
144
                    if bnet.operstate != "ACTIVE":
145
                        backend_mod.create_network(network, backend,
146
                                                   connect=True)
141
                    bnet, jobs =\
142
                        backend_mod.ensure_network_is_active(backend,
143
                                                             network.id)
144
                    if jobs:
147 145
                        msg = ("Sent job to create network '%s' in backend"
148 146
                               " '%s'\n" % (network, backend))
149 147
                        self.stdout.write(msg)
......
163 161
        add_to_backend = options["add_to_backend"]
164 162
        if add_to_backend is not None:
165 163
            backend = get_backend(add_to_backend)
166
            network.create_backend_network(backend=backend)
167
            backend_mod.create_network(network, backend, connect=True)
168
            msg = "Sent job to create network '%s' in backend '%s'\n"
169
            self.stdout.write(msg % (network, backend))
164
            bnet, jobs = backend_mod.ensure_network_is_active(backend,
165
                                                              network.id)
166
            if jobs:
167
                msg = "Sent job to create network '%s' in backend '%s'\n"
168
                self.stdout.write(msg % (network, backend))
170 169

  
171 170
        remove_from_backend = options["remove_from_backend"]
172 171
        if remove_from_backend is not None:
b/snf-cyclades-app/synnefo/logic/backend.py
42 42
from synnefo import quotas
43 43
from synnefo.api.util import release_resource
44 44
from synnefo.util.mac2eui64 import mac2eui64
45
from synnefo.logic.rapi import GanetiApiError
45
from synnefo.logic import rapi
46 46

  
47 47
from logging import getLogger
48 48
log = getLogger(__name__)
......
76 76
    rejected, since they reflect a previous state of the VM.
77 77

  
78 78
    """
79
    if job_status not in ["success", "error", "canceled"]:
79
    if job_status not in rapi.JOB_STATUS_FINALIZED:
80 80
        return vm
81 81

  
82 82
    # Check successful completion of a job will trigger any quotable change in
......
94 94
        # if fails, must be accepted, as the user must manually remove the
95 95
        # failed server
96 96
        serial = vm.serial
97
        if job_status == "success":
97
        if job_status == rapi.JOB_STATUS_SUCCESS:
98 98
            quotas.accept_serial(serial)
99
        elif job_status in ["error", "canceled"]:
99
        elif job_status in [rapi.JOB_STATUS_ERROR, rapi.JOB_STATUS_CANCELED]:
100 100
            log.debug("Job %s failed. Rejecting related serial %s", job_id,
101 101
                      serial)
102 102
            quotas.reject_serial(serial)
103 103
        vm.serial = None
104
    elif job_status == "success" and commission_info is not None:
104
    elif job_status == rapi.JOB_STATUS_SUCCESS and commission_info is not None:
105 105
        log.debug("Expected job was %s. Processing job %s. Commission for"
106 106
                  " this job: %s", vm.task_job_id, job_id, commission_info)
107 107
        # Commission for this change has not been issued, or the issued
......
139 139
    vm.backendopcode = opcode
140 140
    vm.backendlogmsg = logmsg
141 141

  
142
    if status in ["queued", "waiting", "running"]:
142
    if status not in rapi.JOB_STATUS_FINALIZED:
143 143
        vm.save()
144 144
        return
145 145

  
......
148 148
    state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode)
149 149

  
150 150
    # Notifications of success change the operating state
151
    if status == "success":
151
    if status == rapi.JOB_STATUS_SUCCESS:
152 152
        if state_for_success is not None:
153 153
            vm.operstate = state_for_success
154 154
        beparams = job_fields.get("beparams", None)
......
162 162
        # in reversed order.
163 163
        vm.backendtime = etime
164 164

  
165
    if status in ["success", "error", "canceled"] and nics is not None:
165
    if status in rapi.JOB_STATUS_FINALIZED and nics is not None:
166 166
        # Update the NICs of the VM
167 167
        _process_net_status(vm, etime, nics)
168 168

  
169 169
    # Special case: if OP_INSTANCE_CREATE fails --> ERROR
170
    if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
170
    if opcode == 'OP_INSTANCE_CREATE' and status in (rapi.JOB_STATUS_CANCELED,
171
                                                     rapi.JOB_STATUS_ERROR):
171 172
        vm.operstate = 'ERROR'
172 173
        vm.backendtime = etime
173 174
        # Update state of associated NICs
......
176 177
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
177 178
        # when no instance exists at the Ganeti backend.
178 179
        # See ticket #799 for all the details.
179
        if status == 'success' or (status == 'error' and
180
                                   not vm_exists_in_backend(vm)):
180
        if (status == rapi.JOB_STATUS_SUCCESS or
181
           (status == rapi.JOB_STATUS_ERROR and not vm_exists_in_backend(vm))):
181 182
            # VM has been deleted
182 183
            for nic in vm.nics.all():
183 184
                # Release the IP
......
187 188
            vm.deleted = True
188 189
            vm.operstate = state_for_success
189 190
            vm.backendtime = etime
190
            status = "success"
191
            status = rapi.JOB_STATUS_SUCCESS
191 192

  
192
    if status in ["success", "error", "canceled"]:
193
    if status in rapi.JOB_STATUS_FINALIZED:
193 194
        # Job is finalized: Handle quotas/commissioning
194 195
        vm = handle_vm_quotas(vm, job_id=jobid, job_opcode=opcode,
195 196
                              job_status=status, job_fields=job_fields)
......
436 437

  
437 438
    # Notifications of success change the operating state
438 439
    state_for_success = BackendNetwork.OPER_STATE_FROM_OPCODE.get(opcode, None)
439
    if status == 'success' and state_for_success is not None:
440
    if status == rapi.JOB_STATUS_SUCCESS and state_for_success is not None:
440 441
        back_network.operstate = state_for_success
441 442

  
442
    if status in ('canceled', 'error') and opcode == 'OP_NETWORK_ADD':
443
    if (status in (rapi.JOB_STATUS_CANCELED, rapi.JOB_STATUS_ERROR)
444
       and opcode == 'OP_NETWORK_ADD'):
443 445
        back_network.operstate = 'ERROR'
444 446
        back_network.backendtime = etime
445 447

  
446 448
    if opcode == 'OP_NETWORK_REMOVE':
447
        network_is_deleted = (status == "success")
448
        if network_is_deleted or (status == "error" and not
449
        network_is_deleted = (status == rapi.JOB_STATUS_SUCCESS)
450
        if network_is_deleted or (status == rapi.JOB_STATUS_ERROR and not
449 451
                                  network_exists_in_backend(back_network)):
450 452
            back_network.operstate = state_for_success
451 453
            back_network.deleted = True
452 454
            back_network.backendtime = etime
453 455

  
454
    if status == 'success':
456
    if status == rapi.JOB_STATUS_SUCCESS:
455 457
        back_network.backendtime = etime
456 458
    back_network.save()
457 459
    # Also you must update the state of the Network!!
......
547 549
        for ip in add_reserved_ips:
548 550
            network.reserve_address(ip, external=True)
549 551

  
550
    if status == 'success':
552
    if status == rapi.JOB_STATUS_SUCCESS:
551 553
        back_network.backendtime = etime
552 554
    back_network.save()
553 555

  
......
631 633
                   "network": nic.network.backend_id,
632 634
                   "ip": nic.ipv4_address}
633 635
                  for nic in nics]
636

  
634 637
    backend = vm.backend
635 638
    depend_jobs = []
636 639
    for nic in nics:
637
        network = Network.objects.select_for_update().get(id=nic.network_id)
638
        bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
639
                                                             network=network)
640
        if bnet.operstate != "ACTIVE":
641
            depend_jobs = create_network(network, backend, connect=True)
642
    kw["depends"] = [[job, ["success", "error", "canceled"]]
643
                     for job in depend_jobs]
640
        bnet, job_ids = ensure_network_is_active(backend, nic.network_id)
641
        depend_jobs.extend(job_ids)
642

  
643
    kw["depends"] = create_job_dependencies(depend_jobs)
644 644

  
645 645
    # Defined in settings.GANETI_CREATEINSTANCE_KWARGS
646 646
    # kw['os'] = settings.GANETI_OS_PROVIDER
......
753 753
    try:
754 754
        get_instance_info(vm)
755 755
        return True
756
    except GanetiApiError as e:
756
    except rapi.GanetiApiError as e:
757 757
        if e.code == 404:
758 758
            return False
759 759
        raise e
......
768 768
    try:
769 769
        get_network_info(backend_network)
770 770
        return True
771
    except GanetiApiError as e:
771
    except rapi.GanetiApiError as e:
772 772
        if e.code == 404:
773 773
            return False
774 774

  
775 775

  
776
def ensure_network_is_active(backend, network_id):
777
    """Ensure that a network is active in the specified backend
778

  
779
    Check that a network exists and is active in the specified backend. If not
780
    (re-)create the network. Return the corresponding BackendNetwork object
781
    and the IDs of the Ganeti job to create the network.
782

  
783
    """
784
    network = Network.objects.select_for_update().get(id=network_id)
785
    bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
786
                                                         network=network)
787
    job_ids = []
788
    if bnet.operstate != "ACTIVE":
789
        job_ids = create_network(network, backend, connect=True)
790

  
791
    return bnet, job_ids
792

  
793

  
776 794
def create_network(network, backend, connect=True):
777 795
    """Create a network in a Ganeti backend"""
778 796
    log.debug("Creating network %s in backend %s", network, backend)
......
845 863
    else:
846 864
        conflicts_check = False
847 865

  
848
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
866
    depends = create_job_dependencies(depends)
849 867
    with pooled_rapi_client(backend) as client:
850 868
        groups = [group] if group is not None else client.GetGroups()
851 869
        job_ids = []
......
868 886

  
869 887

  
870 888
def _delete_network(network, backend, depends=[]):
871
    depends = [[job, ["success", "error", "canceled"]] for job in depends]
889
    depends = create_job_dependencies(depends)
872 890
    with pooled_rapi_client(backend) as client:
873 891
        return client.DeleteNetwork(network.backend_id, depends)
874 892

  
......
888 906
def connect_to_network(vm, nic):
889 907
    network = nic.network
890 908
    backend = vm.backend
891
    network = Network.objects.select_for_update().get(id=network.id)
892
    bnet, created = BackendNetwork.objects.get_or_create(backend=backend,
893
                                                         network=network)
894
    depend_jobs = []
895
    if bnet.operstate != "ACTIVE":
896
        depend_jobs = create_network(network, backend, connect=True)
909
    bnet, depend_jobs = ensure_network_is_active(backend, network.id)
897 910

  
898
    depends = [[job, ["success", "error", "canceled"]] for job in depend_jobs]
911
    depends = create_job_dependencies(depend_jobs)
899 912

  
900 913
    nic = {'name': nic.backend_uuid,
901 914
           'network': network.backend_id,
......
1071 1084

  
1072 1085
def create_network_synced(network, backend):
1073 1086
    result = _create_network_synced(network, backend)
1074
    if result[0] != 'success':
1087
    if result[0] != rapi.JOB_STATUS_SUCCESS:
1075 1088
        return result
1076 1089
    result = connect_network_synced(network, backend)
1077 1090
    return result
......
1090 1103
            job = client.ConnectNetwork(network.backend_id, group,
1091 1104
                                        network.mode, network.link)
1092 1105
            result = wait_for_job(client, job)
1093
            if result[0] != 'success':
1106
            if result[0] != rapi.JOB_STATUS_SUCCESS:
1094 1107
                return result
1095 1108

  
1096 1109
    return result
......
1099 1112
def wait_for_job(client, jobid):
1100 1113
    result = client.WaitForJobChange(jobid, ['status', 'opresult'], None, None)
1101 1114
    status = result['job_info'][0]
1102
    while status not in ['success', 'error', 'cancel']:
1115
    while status not in rapi.JOB_STATUS_FINALIZED:
1103 1116
        result = client.WaitForJobChange(jobid, ['status', 'opresult'],
1104 1117
                                         [result], None)
1105 1118
        status = result['job_info'][0]
1106 1119

  
1107
    if status == 'success':
1120
    if status == rapi.JOB_STATUS_SUCCESS:
1108 1121
        return (status, None)
1109 1122
    else:
1110 1123
        error = result['job_info'][1]
1111 1124
        return (status, error)
1125

  
1126

  
1127
def create_job_dependencies(job_ids=[], job_states=None):
1128
    """Transform a list of job IDs to Ganeti 'depends' attribute."""
1129
    if job_states is None:
1130
        job_states = list(rapi.JOB_STATUS_FINALIZED)
1131
    assert(type(job_states) == list)
1132
    return [[job_id, job_states] for job_id in job_ids]

Also available in: Unified diff