Revision 198d91c3

b/snf-cyclades-app/synnefo/db/models.py
743 743

  
744 744
@contextmanager
745 745
def pooled_rapi_client(obj):
746
        if isinstance(obj, VirtualMachine):
746
        if isinstance(obj, (VirtualMachine, BackendNetwork)):
747 747
            backend = obj.backend
748 748
        else:
749 749
            backend = obj
b/snf-cyclades-app/synnefo/logic/backend.py
41 41
from synnefo import quotas
42 42
from synnefo.api.util import release_resource
43 43
from synnefo.util.mac2eui64 import mac2eui64
44
from synnefo.logic.rapi import GanetiApiError
44 45

  
45 46
from logging import getLogger
46 47
log = getLogger(__name__)
......
87 88
        vm.operstate = 'ERROR'
88 89
        vm.backendtime = etime
89 90
    elif opcode == 'OP_INSTANCE_REMOVE':
90
        # Set the deleted flag explicitly, cater for admin-initiated removals
91 91
        # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR,
92 92
        # when no instance exists at the Ganeti backend.
93
        # See ticket #799 for all the details.
94
        #
95
        if (status == 'success' or
96
           (status == 'error' and (vm.operstate == 'ERROR' or
97
                                   vm.action == 'DESTROY'))):
93
        if status == "success" or (status == "error" and
94
                                   not vm_exists_in_backend(vm)):
98 95
            _process_net_status(vm, etime, nics=[])
99 96
            vm.deleted = True
100 97
            vm.operstate = state_for_success
......
233 230
        back_network.backendtime = etime
234 231

  
235 232
    if opcode == 'OP_NETWORK_REMOVE':
236
        if (status == 'success' or
237
           (status == 'error' and (back_network.operstate == 'ERROR' or
238
                                   network.action == 'DESTROY'))):
233
        network_is_deleted = (status == "success")
234
        if network_is_deleted or (status == "error" and not
235
                                  network_exists_in_backend(back_network)):
239 236
            back_network.operstate = state_for_success
240 237
            back_network.deleted = True
241 238
            back_network.backendtime = etime
......
489 486

  
490 487
def get_instance_info(vm):
491 488
    with pooled_rapi_client(vm) as client:
492
        return client.GetInstanceInfo(vm.backend_vm_id)
489
        return client.GetInstance(vm.backend_vm_id)
490

  
491

  
492
def vm_exists_in_backend(vm):
493
    try:
494
        get_instance_info(vm)
495
        return True
496
    except GanetiApiError as e:
497
        if e.code == 404:
498
            return False
499
        raise e
500

  
501

  
502
def get_network_info(backend_network):
503
    with pooled_rapi_client(backend_network) as client:
504
        return client.GetNetwork(backend_network.network.backend_id)
505

  
506

  
507
def network_exists_in_backend(backend_network):
508
    try:
509
        get_network_info(backend_network)
510
        return True
511
    except GanetiApiError as e:
512
        if e.code == 404:
513
            return False
493 514

  
494 515

  
495 516
def create_network(network, backend, connect=True):
b/snf-cyclades-app/synnefo/logic/tests.py
44 44
from synnefo.logic.callbacks import (update_db, update_network,
45 45
                                     update_build_progress)
46 46
from snf_django.utils.testing import mocked_quotaholder
47
from synnefo.logic.rapi import GanetiApiError
47 48

  
48 49
now = datetime.now
49 50
from time import time
......
145 146
        # Check that nics are deleted
146 147
        self.assertFalse(db_vm.nics.all())
147 148

  
149
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
150
    def test_remove_error(self, rapi, client):
151
        vm = mfactory.VirtualMachineFactory()
152
        # Also create a NIC
153
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
154
                              status="error",
155
                              instance=vm.backend_vm_id)
156
        rapi().GetInstance.return_value = {}
157
        update_db(client, msg)
158
        db_vm = VirtualMachine.objects.get(id=vm.id)
159
        self.assertFalse(db_vm.deleted)
160

  
161
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
162
                                                        code=503)
163
        update_db(client, msg)
164
        db_vm = VirtualMachine.objects.get(id=vm.id)
165
        self.assertFalse(db_vm.deleted)
166

  
167
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
168
                                                        code=404)
169
        with mocked_quotaholder():
170
            update_db(client, msg)
171
        db_vm = VirtualMachine.objects.get(id=vm.id)
172
        self.assertTrue(db_vm.deleted)
173

  
148 174
    def test_create(self, client):
149 175
        vm = mfactory.VirtualMachineFactory()
150 176
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
......
418 444
                    pool = MacPrefixPoolTable.get_pool()
419 445
                    self.assertTrue(pool.is_available(net.mac_prefix))
420 446

  
447
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
448
    def test_remove_error(self, rapi, client):
449
        mfactory.MacPrefixPoolTableFactory()
450
        mfactory.BridgePoolTableFactory()
451
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
452
        network = bn.network
453
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
454
                              network=network.backend_id,
455
                              status="error",
456
                              cluster=bn.backend.clustername)
457
        rapi().GetNetwork.return_value = {}
458
        update_network(client, msg)
459
        bn = BackendNetwork.objects.get(id=bn.id)
460
        self.assertNotEqual(bn.operstate, "DELETED")
461
        rapi().GetNetwork.side_effect = GanetiApiError(msg="foo", code=404)
462
        with mocked_quotaholder():
463
            update_network(client, msg)
464
        bn = BackendNetwork.objects.get(id=bn.id)
465
        self.assertEqual(bn.operstate, "DELETED")
466

  
421 467
    def test_remove_offline_backend(self, client):
422 468
        """Test network removing when a backend is offline"""
423 469
        mfactory.BridgePoolTableFactory()

Also available in: Unified diff