Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests / reconciliation.py @ ef0839e9

History | View | Annotate | Download (14.4 kB)

1
# vim: set fileencoding=utf-8 :
2
# Copyright 2012 GRNET S.A. All rights reserved.
3
#
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions
6
# are met:
7
#
8
#   1. Redistributions of source code must retain the above copyright
9
#      notice, this list of conditions and the following disclaimer.
10
#
11
#  2. Redistributions in binary form must reproduce the above copyright
12
#     notice, this list of conditions and the following disclaimer in the
13
#     documentation and/or other materials provided with the distribution.
14
#
15
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
# SUCH DAMAGE.
26
#
27
# The views and conclusions contained in the software and documentation are
28
# those of the authors and should not be interpreted as representing official
29
# policies, either expressed or implied, of GRNET S.A.
30
import logging
31
from django.test import TestCase
32

    
33
from synnefo.db.models import VirtualMachine, Network, BackendNetwork
34
from synnefo.db import models_factory as mfactory
35
from synnefo.logic import reconciliation
36
from mock import patch
37
from snf_django.utils.testing import mocked_quotaholder
38
from time import time
39
from synnefo import settings
40

    
41

    
42
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
43
class ServerReconciliationTest(TestCase):
44
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
45
    def setUp(self, mrapi):
46
        self.backend = mfactory.BackendFactory()
47
        log = logging.getLogger()
48
        options = {"fix_unsynced": True,
49
                   "fix_stale": True,
50
                   "fix_orphans": True,
51
                   "fix_unsynced_nics": True,
52
                   "fix_unsynced_flavors": True}
53
        self.reconciler = reconciliation.BackendReconciler(self.backend,
54
                                                           options=options,
55
                                                           logger=log)
56

    
57
    def test_building_vm(self, mrapi):
58
        vm1 = mfactory.VirtualMachineFactory(backend=self.backend,
59
                                             backendjobid=1,
60
                                             operstate="BUILD")
61
        for status in ["queued", "waiting", "running"]:
62
            mrapi().GetJobs.return_value = [{"id": "1", "status": status,
63
                                             "end_ts": None}]
64
            with mocked_quotaholder():
65
                self.reconciler.reconcile()
66
            vm1 = VirtualMachine.objects.get(id=vm1.id)
67
            self.assertFalse(vm1.deleted)
68
            self.assertEqual(vm1.operstate, "BUILD")
69

    
70
        mrapi().GetJobs.return_value = [{"id": "1", "status": "error",
71
                                         "end_ts": [44123, 1]}]
72
        with mocked_quotaholder():
73
            self.reconciler.reconcile()
74
        vm1 = VirtualMachine.objects.get(id=vm1.id)
75
        self.assertFalse(vm1.deleted)
76
        self.assertEqual(vm1.operstate, "ERROR")
77

    
78
        for status in ["success", "canceled"]:
79
            vm1.operstate = "BUILD"
80
            vm1.deleted = False
81
            vm1.save()
82
            mrapi().GetJobs.return_value = [{"id": "1", "status": status,
83
                                            "end_ts": [44123, 1]}]
84
            with mocked_quotaholder():
85
                self.reconciler.reconcile()
86
            vm1 = VirtualMachine.objects.get(id=vm1.id)
87
            self.assertFalse(vm1.deleted)
88
            self.assertEqual(vm1.operstate, "ERROR")
89

    
90
    def test_stale_server(self, mrapi):
91
        mrapi.GetInstances = []
92
        vm1 = mfactory.VirtualMachineFactory(backend=self.backend,
93
                                             deleted=False,
94
                                             operstate="ERROR")
95

    
96
        with mocked_quotaholder():
97
            self.reconciler.reconcile()
98
        vm1 = VirtualMachine.objects.get(id=vm1.id)
99
        self.assertFalse(vm1.deleted)
100
        vm2 = mfactory.VirtualMachineFactory(backend=self.backend,
101
                                             deleted=False,
102
                                             action="DESTROY",
103
                                             operstate="ERROR")
104
        with mocked_quotaholder():
105
            self.reconciler.reconcile()
106
        vm2 = VirtualMachine.objects.get(id=vm2.id)
107
        self.assertTrue(vm2.deleted)
108
        vm3 = mfactory.VirtualMachineFactory(backend=self.backend,
109
                                             deleted=False,
110
                                             action="DESTROY",
111
                                             operstate="ACTIVE")
112
        with mocked_quotaholder():
113
            self.reconciler.reconcile()
114
        vm3 = VirtualMachine.objects.get(id=vm3.id)
115
        self.assertTrue(vm3.deleted)
116

    
117
    def test_orphan_server(self, mrapi):
118
        cmrapi = self.reconciler.client
119
        mrapi().GetInstances.return_value =\
120
            [{"name": "%s22" % settings.BACKEND_PREFIX_ID,
121
             "beparams": {"maxmem": 1024,
122
                          "minmem": 1024,
123
                          "vcpus": 4},
124
             "oper_state": True,
125
             "mtime": time(),
126
             "disk.sizes": [],
127
             "disk.names": [],
128
             "disk.uuids": [],
129
             "nic.ips": [],
130
             "nic.names": [],
131
             "nic.macs": [],
132
             "nic.networks.names": [],
133
             "tags": []}]
134
        self.reconciler.reconcile()
135
        cmrapi.DeleteInstance\
136
              .assert_called_once_with("%s22" % settings.BACKEND_PREFIX_ID)
137

    
138
    def test_unsynced_operstate(self, mrapi):
139
        vm1 = mfactory.VirtualMachineFactory(backend=self.backend,
140
                                             deleted=False,
141
                                             operstate="STOPPED")
142
        mrapi().GetInstances.return_value =\
143
            [{"name": vm1.backend_vm_id,
144
             "beparams": {"maxmem": 1024,
145
                          "minmem": 1024,
146
                          "vcpus": 4},
147
             "oper_state": True,
148
             "mtime": time(),
149
             "disk.sizes": [],
150
             "disk.names": [],
151
             "disk.uuids": [],
152
             "nic.ips": [],
153
             "nic.names": [],
154
             "nic.macs": [],
155
             "nic.networks.names": [],
156
             "tags": []}]
157
        with mocked_quotaholder():
158
            self.reconciler.reconcile()
159
        vm1 = VirtualMachine.objects.get(id=vm1.id)
160
        self.assertEqual(vm1.operstate, "STARTED")
161

    
162
    def test_unsynced_flavor(self, mrapi):
163
        flavor1 = mfactory.FlavorFactory(cpu=2, ram=1024, disk=1,
164
                                         disk_template="drbd")
165
        flavor2 = mfactory.FlavorFactory(cpu=4, ram=2048, disk=1,
166
                                         disk_template="drbd")
167
        vm1 = mfactory.VirtualMachineFactory(backend=self.backend,
168
                                             deleted=False,
169
                                             flavor=flavor1,
170
                                             operstate="STARTED")
171
        mrapi().GetInstances.return_value =\
172
            [{"name": vm1.backend_vm_id,
173
             "beparams": {"maxmem": 2048,
174
                          "minmem": 2048,
175
                          "vcpus": 4},
176
             "oper_state": True,
177
             "mtime": time(),
178
             "disk.sizes": [],
179
             "disk.names": [],
180
             "disk.uuids": [],
181
             "nic.ips": [],
182
             "nic.names": [],
183
             "nic.macs": [],
184
             "nic.networks.names": [],
185
             "tags": []}]
186
        with mocked_quotaholder():
187
            self.reconciler.reconcile()
188
        vm1 = VirtualMachine.objects.get(id=vm1.id)
189
        self.assertEqual(vm1.flavor, flavor2)
190
        self.assertEqual(vm1.operstate, "STARTED")
191

    
192
    def test_unsynced_nics(self, mrapi):
193
        network1 = mfactory.NetworkWithSubnetFactory(
194
            subnet__cidr="10.0.0.0/24", subnet__gateway="10.0.0.2")
195
        network2 = mfactory.NetworkWithSubnetFactory(
196
            subnet__cidr="192.168.2.0/24", subnet__gateway="192.168.2.2")
197
        vm1 = mfactory.VirtualMachineFactory(backend=self.backend,
198
                                             deleted=False,
199
                                             operstate="STOPPED")
200
        subnet = network1.subnets.get(ipversion=4)
201
        ip = mfactory.IPv4AddressFactory(nic__machine=vm1, network=network1,
202
                                         subnet=subnet,
203
                                         address="10.0.0.3")
204
        nic = ip.nic
205
        mrapi().GetInstances.return_value =\
206
            [{"name": vm1.backend_vm_id,
207
             "beparams": {"maxmem": 2048,
208
                          "minmem": 2048,
209
                          "vcpus": 4},
210
             "oper_state": True,
211
             "mtime": time(),
212
             "disk.sizes": [],
213
             "disk.names": [],
214
             "disk.uuids": [],
215
             "nic.names": [nic.backend_uuid],
216
             "nic.ips": ["192.168.2.5"],
217
             "nic.macs": ["aa:00:bb:cc:dd:ee"],
218
             "nic.networks.names": [network2.backend_id],
219
             "tags": []}]
220
        with mocked_quotaholder():
221
            self.reconciler.reconcile()
222
        vm1 = VirtualMachine.objects.get(id=vm1.id)
223
        self.assertEqual(vm1.operstate, "STARTED")
224
        nic = vm1.nics.all()[0]
225
        self.assertEqual(nic.network, network2)
226
        self.assertEqual(nic.ipv4_address, "192.168.2.5")
227
        self.assertEqual(nic.mac, "aa:00:bb:cc:dd:ee")
228

    
229

    
230
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
231
class NetworkReconciliationTest(TestCase):
232
    def setUp(self):
233
        self.backend = mfactory.BackendFactory()
234
        log = logging.getLogger()
235
        self.reconciler = reconciliation.NetworkReconciler(
236
            logger=log,
237
            fix=True)
238

    
239
    def test_parted_network(self, mrapi):
240
        net1 = mfactory.NetworkWithSubnetFactory(public=False)
241
        mrapi().GetNetworks.return_value = []
242
        # Test nothing if Ganeti returns nothing
243
        self.assertEqual(net1.backend_networks.count(), 0)
244
        self.reconciler.reconcile_networks()
245
        self.assertEqual(net1.backend_networks.count(), 0)
246

    
247
        # Test creation if exists in Ganeti
248
        self.assertEqual(net1.backend_networks.count(), 0)
249
        mrapi().GetNetworks.return_value = [{"name": net1.backend_id,
250
                                             "group_list": [["default",
251
                                                             "bridged",
252
                                                             "prv0"]],
253
                                             "network": net1.subnet4.cidr,
254
                                             "map": "....",
255
                                             "external_reservations": ""}]
256
        self.reconciler.reconcile_networks()
257
        self.assertTrue(net1.backend_networks
258
                        .filter(backend=self.backend).exists())
259

    
260
    def test_stale_network(self, mrapi):
261
        # Test that stale network will be deleted from DB, if network action is
262
        # destroy
263
        net1 = mfactory.NetworkWithSubnetFactory(public=False,
264
                                                 flavor="IP_LESS_ROUTED",
265
                                                 action="DESTROY",
266
                                                 deleted=False)
267
        bn1 = mfactory.BackendNetworkFactory(network=net1,
268
                                             backend=self.backend,
269
                                             operstate="ACTIVE")
270
        mrapi().GetNetworks.return_value = []
271
        self.assertFalse(net1.deleted)
272
        with mocked_quotaholder():
273
            self.reconciler.reconcile_networks()
274
        net1 = Network.objects.get(id=net1.id)
275
        self.assertTrue(net1.deleted)
276
        self.assertFalse(net1.backend_networks.filter(id=bn1.id).exists())
277
        # But not if action is not DESTROY
278
        net2 = mfactory.NetworkWithSubnetFactory(public=False, action="CREATE")
279
        mfactory.BackendNetworkFactory(network=net2, backend=self.backend)
280
        self.assertFalse(net2.deleted)
281
        self.reconciler.reconcile_networks()
282
        self.assertFalse(net2.deleted)
283

    
284
    def test_missing_network(self, mrapi):
285
        net2 = mfactory.NetworkWithSubnetFactory(public=False, action="CREATE")
286
        mfactory.BackendNetworkFactory(network=net2, backend=self.backend)
287
        mrapi().GetNetworks.return_value = []
288
        self.reconciler.reconcile_networks()
289
        self.assertEqual(len(mrapi().CreateNetwork.mock_calls), 1)
290

    
291
    #def test_hanging_networks(self, mrapi):
292
    #    pass
293

    
294
    def test_unsynced_networks(self, mrapi):
295
        net = mfactory.NetworkWithSubnetFactory(public=False, state="PENDING",
296
                                                action="CREATE", deleted=False)
297
        bn = mfactory.BackendNetworkFactory(network=net, backend=self.backend,
298
                                            operstate="PENDING")
299
        mrapi().GetNetworks.return_value = [{"name": net.backend_id,
300
                                             "group_list": [],
301
                                             "network": net.subnet4.cidr,
302
                                             "map": "....",
303
                                             "external_reservations": ""}]
304
        self.assertEqual(bn.operstate, "PENDING")
305
        self.reconciler.reconcile_networks()
306
        bn = BackendNetwork.objects.get(id=bn.id)
307
        self.assertEqual(bn.operstate, "ACTIVE")
308

    
309
    def test_orphan_networks(self, mrapi):
310
        net = mfactory.NetworkWithSubnetFactory(public=False, action="CREATE",
311
                                                deleted=True)
312
        mrapi().GetNetworks.return_value = [{"name": net.backend_id,
313
                                             "group_list": [],
314
                                             "network": net.subnet4.cidr,
315
                                             "map": "....",
316
                                             "external_reservations": ""}]
317
        self.reconciler.reconcile_networks()
318
        mrapi().DeleteNetwork.assert_called_once_with(net.backend_id, [])