~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/tests/compute/test_resource_tracker.py

  • Committer: Package Import Robot
  • Author(s): Adam Gandelman, Adam Gandelman, Chuck Short
  • Date: 2012-08-27 15:37:18 UTC
  • mfrom: (1.1.60)
  • Revision ID: package-import@ubuntu.com-20120827153718-lj8er44eqqz1gsrj
Tags: 2012.2~rc1~20120827.15815-0ubuntu1
[ Adam Gandelman ]
* New upstream release.

[ Chuck Short ]
* debian/patches/0001-Update-tools-hacking-for-pep8-1.2-and-
  beyond.patch: Dropped we dont run pep8 tests anymore.
* debian/control: Drop pep8 build depends
* debian/*.upstart.in: Make sure we transition correctly from runlevel
  1 to 2. (LP: #820694)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright (c) 2012 OpenStack, LLC.
 
4
# All Rights Reserved.
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
 
 
18
"""Tests for compute resource tracking"""
 
19
 
 
20
import copy
 
21
 
 
22
from nova.compute import resource_tracker
 
23
from nova.compute import task_states
 
24
from nova.compute import vm_states
 
25
from nova import db
 
26
from nova import exception
 
27
from nova.openstack.common import timeutils
 
28
from nova import test
 
29
from nova.virt import driver
 
30
 
 
31
 
 
32
class FakeContext(object):
 
33
    def __init__(self, is_admin=False):
 
34
        self.is_admin = is_admin
 
35
 
 
36
    def elevated(self):
 
37
        return FakeContext(is_admin=True)
 
38
 
 
39
 
 
40
class UnsupportedVirtDriver(driver.ComputeDriver):
 
41
    """Pretend version of a lame virt driver"""
 
42
    def get_available_resource(self):
 
43
        # no support for getting resource usage info
 
44
        return {}
 
45
 
 
46
 
 
47
class FakeVirtDriver(driver.ComputeDriver):
 
48
 
 
49
    def __init__(self):
 
50
        self.memory_mb = 5
 
51
        self.local_gb = 6
 
52
        self.vcpus = 1
 
53
 
 
54
        self.memory_mb_used = 0
 
55
        self.local_gb_used = 0
 
56
 
 
57
    def get_available_resource(self):
 
58
        d = {
 
59
            'vcpus': self.vcpus,
 
60
            'memory_mb': self.memory_mb,
 
61
            'local_gb': self.local_gb,
 
62
            'vcpus_used': 0,
 
63
            'memory_mb_used': self.memory_mb_used,
 
64
            'local_gb_used': self.local_gb_used,
 
65
            'hypervisor_type': 'fake',
 
66
            'hypervisor_version': 0,
 
67
            'hypervisor_hostname': 'fakehost',
 
68
            'cpu_info': '',
 
69
        }
 
70
        return d
 
71
 
 
72
 
 
73
class BaseTestCase(test.TestCase):
 
74
 
 
75
    def setUp(self):
 
76
        super(BaseTestCase, self).setUp()
 
77
 
 
78
        self.context = FakeContext()
 
79
 
 
80
        self.instance_ref = {
 
81
            "memory_mb": 1,
 
82
            "root_gb": 1,
 
83
            "ephemeral_gb": 1,
 
84
            "vm_state": vm_states.BUILDING,
 
85
            "task_state": None,
 
86
            "os_type": "Linux",
 
87
            "project_id": "1234",
 
88
            "vcpus": 1,
 
89
        }
 
90
 
 
91
        self.stubs.Set(db, 'instance_get_all_by_filters',
 
92
                self._fake_instance_get_all_by_filters)
 
93
 
 
94
    def _create_compute_node(self, values=None):
 
95
        compute = {
 
96
            "id": 1,
 
97
            "service_id": 1,
 
98
            "vcpus": 1,
 
99
            "memory_mb": 1,
 
100
            "local_gb": 1,
 
101
            "vcpus_used": 1,
 
102
            "memory_mb_used": 1,
 
103
            "local_gb_used": 1,
 
104
            "free_ram_mb": 1,
 
105
            "free_disk_gb": 1,
 
106
            "current_workload": 1,
 
107
            "running_vms": 0,
 
108
            "cpu_info": None,
 
109
            "stats": [{"key": "num_instances", "value": "1"}]
 
110
        }
 
111
        if values:
 
112
            compute.update(values)
 
113
        return compute
 
114
 
 
115
    def _create_service(self, host="fakehost", compute=None):
 
116
        if compute:
 
117
            compute = [compute]
 
118
 
 
119
        service = {
 
120
            "id": 1,
 
121
            "host": host,
 
122
            "binary": "nova-compute",
 
123
            "topic": "compute",
 
124
            "compute_node": compute,
 
125
        }
 
126
        return service
 
127
 
 
128
    def _fake_instance_get_all_by_filters(self, ctx, filters, **kwargs):
 
129
        return []
 
130
 
 
131
    def _tracker(self, unsupported=False):
 
132
        host = "fakehost"
 
133
 
 
134
        if unsupported:
 
135
            driver = UnsupportedVirtDriver()
 
136
        else:
 
137
            driver = FakeVirtDriver()
 
138
 
 
139
        tracker = resource_tracker.ResourceTracker(host, driver)
 
140
        return tracker
 
141
 
 
142
 
 
143
class UnsupportedDriverTestCase(BaseTestCase):
 
144
    """Resource tracking should be disabled when the virt driver doesn't
 
145
    support it.
 
146
    """
 
147
    def setUp(self):
 
148
        super(UnsupportedDriverTestCase, self).setUp()
 
149
        self.tracker = self._tracker(unsupported=True)
 
150
        # seed tracker with data:
 
151
        self.tracker.update_available_resource(self.context)
 
152
 
 
153
    def testDisabled(self):
 
154
        # disabled = no compute node stats
 
155
        self.assertTrue(self.tracker.disabled)
 
156
        self.assertEqual(None, self.tracker.compute_node)
 
157
 
 
158
    def testDisabledClaim(self):
 
159
        # basic claim:
 
160
        claim = self.tracker.begin_resource_claim(self.context, 1, 1)
 
161
        self.assertEqual(None, claim)
 
162
 
 
163
    def testDisabledContextClaim(self):
 
164
        # basic context manager variation:
 
165
        with self.tracker.resource_claim(self.context, 1, 1):
 
166
            pass
 
167
        self.assertEqual(0, len(self.tracker.claims))
 
168
 
 
169
    def testDisabledInstanceClaim(self):
 
170
        # instance variation:
 
171
        claim = self.tracker.begin_instance_resource_claim(self.context,
 
172
                self.instance_ref)
 
173
        self.assertEqual(None, claim)
 
174
 
 
175
    def testDisabledInstanceContextClaim(self):
 
176
        # instance context manager variation:
 
177
        with self.tracker.instance_resource_claim(self.context,
 
178
                self.instance_ref):
 
179
            pass
 
180
        self.assertEqual(0, len(self.tracker.claims))
 
181
 
 
182
    def testDisabledFinishClaim(self):
 
183
        self.assertEqual(None, self.tracker.finish_resource_claim(None))
 
184
 
 
185
    def testDisabledAbortClaim(self):
 
186
        self.assertEqual(None, self.tracker.abort_resource_claim(self.context,
 
187
            None))
 
188
 
 
189
    def testDisabledFreeResources(self):
 
190
        self.tracker.free_resources(self.context)
 
191
        self.assertTrue(self.tracker.disabled)
 
192
        self.assertEqual(None, self.tracker.compute_node)
 
193
 
 
194
 
 
195
class MissingServiceTestCase(BaseTestCase):
 
196
    def setUp(self):
 
197
        super(MissingServiceTestCase, self).setUp()
 
198
        self.context = FakeContext(is_admin=True)
 
199
        self.tracker = self._tracker()
 
200
 
 
201
    def testMissingService(self):
 
202
        """No service record in DB."""
 
203
        self.tracker.update_available_resource(self.context)
 
204
        self.assertTrue(self.tracker.disabled)
 
205
 
 
206
 
 
207
class MissingComputeNodeTestCase(BaseTestCase):
 
208
    def setUp(self):
 
209
        super(MissingComputeNodeTestCase, self).setUp()
 
210
        self.tracker = self._tracker()
 
211
 
 
212
        self.stubs.Set(db, 'service_get_all_compute_by_host',
 
213
                self._fake_service_get_all_compute_by_host)
 
214
        self.stubs.Set(db, 'compute_node_create',
 
215
                self._fake_create_compute_node)
 
216
 
 
217
    def _fake_create_compute_node(self, context, values):
 
218
        self.created = True
 
219
        return self._create_compute_node()
 
220
 
 
221
    def _fake_service_get_all_compute_by_host(self, ctx, host):
 
222
        # return a service with no joined compute
 
223
        service = self._create_service()
 
224
        return [service]
 
225
 
 
226
    def testCreatedComputeNode(self):
 
227
        self.tracker.update_available_resource(self.context)
 
228
        self.assertTrue(self.created)
 
229
 
 
230
    def testEnabled(self):
 
231
        self.tracker.update_available_resource(self.context)
 
232
        self.assertFalse(self.tracker.disabled)
 
233
 
 
234
 
 
235
class ResourceTestCase(BaseTestCase):
 
236
    def setUp(self):
 
237
        super(ResourceTestCase, self).setUp()
 
238
        self.tracker = self._tracker()
 
239
        self.stubs.Set(db, 'service_get_all_compute_by_host',
 
240
                self._fake_service_get_all_compute_by_host)
 
241
        self.stubs.Set(db, 'compute_node_update',
 
242
                self._fake_compute_node_update)
 
243
 
 
244
        self.tracker.update_available_resource(self.context)
 
245
 
 
246
    def _fake_service_get_all_compute_by_host(self, ctx, host):
 
247
        self.compute = self._create_compute_node()
 
248
        self.service = self._create_service(host, compute=self.compute)
 
249
        return [self.service]
 
250
 
 
251
    def _fake_compute_node_update(self, ctx, compute_node_id, values,
 
252
            prune_stats=False):
 
253
        self.updated = True
 
254
        values['stats'] = [{"key": "num_instances", "value": "1"}]
 
255
        self.compute.update(values)
 
256
        return self.compute
 
257
 
 
258
    def testFreeRamResourceValue(self):
 
259
        driver = FakeVirtDriver()
 
260
        mem_free = driver.memory_mb - driver.memory_mb_used
 
261
        self.assertEqual(mem_free, self.tracker.compute_node['free_ram_mb'])
 
262
 
 
263
    def testFreeDiskResourceValue(self):
 
264
        driver = FakeVirtDriver()
 
265
        mem_free = driver.local_gb - driver.local_gb_used
 
266
        self.assertEqual(mem_free, self.tracker.compute_node['free_disk_gb'])
 
267
 
 
268
    def testUpdateComputeNode(self):
 
269
        self.assertFalse(self.tracker.disabled)
 
270
        self.assertTrue(self.updated)
 
271
 
 
272
    def testInsufficientMemoryClaim(self):
 
273
        """Exceed memory limit of 5MB"""
 
274
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=2,
 
275
                disk_gb=0)
 
276
        self.assertNotEqual(None, claim)
 
277
 
 
278
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=3,
 
279
                disk_gb=0)
 
280
        self.assertNotEqual(None, claim)
 
281
 
 
282
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=1,
 
283
                disk_gb=0)
 
284
        self.assertEqual(None, claim)
 
285
 
 
286
    def testInsufficientMemoryClaimWithOversubscription(self):
 
287
        """Exceed oversubscribed memory limit of 10MB"""
 
288
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=10,
 
289
                disk_gb=0, memory_mb_limit=10)
 
290
        self.assertNotEqual(None, claim)
 
291
 
 
292
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=1,
 
293
                disk_gb=0, memory_mb_limit=10)
 
294
        self.assertEqual(None, claim)
 
295
 
 
296
    def testInsufficientDiskClaim(self):
 
297
        """Exceed disk limit of 5GB"""
 
298
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=0,
 
299
                disk_gb=2)
 
300
        self.assertNotEqual(None, claim)
 
301
 
 
302
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=0,
 
303
                disk_gb=3)
 
304
        self.assertNotEqual(None, claim)
 
305
 
 
306
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=0,
 
307
                disk_gb=5)
 
308
        self.assertEqual(None, claim)
 
309
 
 
310
    def testClaimAndFinish(self):
 
311
        self.assertEqual(5, self.tracker.compute_node['memory_mb'])
 
312
        self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
 
313
 
 
314
        self.assertEqual(6, self.tracker.compute_node['local_gb'])
 
315
        self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
 
316
 
 
317
        claim_mem = 3
 
318
        claim_disk = 2
 
319
        claim = self.tracker.begin_resource_claim(self.context, claim_mem,
 
320
                claim_disk)
 
321
 
 
322
        self.assertEqual(5, self.compute["memory_mb"])
 
323
        self.assertEqual(claim_mem, self.compute["memory_mb_used"])
 
324
        self.assertEqual(5 - claim_mem, self.compute["free_ram_mb"])
 
325
 
 
326
        self.assertEqual(6, self.compute["local_gb"])
 
327
        self.assertEqual(claim_disk, self.compute["local_gb_used"])
 
328
        self.assertEqual(6 - claim_disk, self.compute["free_disk_gb"])
 
329
 
 
330
        # 1st pretend that the compute operation finished and claimed the
 
331
        # desired resources from the virt layer
 
332
        driver = self.tracker.driver
 
333
        driver.memory_mb_used = claim_mem
 
334
        driver.local_gb_used = claim_disk
 
335
 
 
336
        # 2nd update compute node from the virt layer.  because the claim is
 
337
        # in-progress (unfinished), the audit will actually mark the resources
 
338
        # as unsubscribed:
 
339
        self.tracker.update_available_resource(self.context)
 
340
 
 
341
        self.assertEqual(2 * claim_mem,
 
342
                self.compute['memory_mb_used'])
 
343
        self.assertEqual(5 - (2 * claim_mem),
 
344
                self.compute['free_ram_mb'])
 
345
 
 
346
        self.assertEqual(2 * claim_disk,
 
347
                self.compute['local_gb_used'])
 
348
        self.assertEqual(6 - (2 * claim_disk),
 
349
                self.compute['free_disk_gb'])
 
350
 
 
351
        # Finally, finish the claimm and update from the virt layer again.
 
352
        # Resource usage will be consistent again:
 
353
        self.tracker.finish_resource_claim(claim)
 
354
        self.tracker.update_available_resource(self.context)
 
355
 
 
356
        self.assertEqual(claim_mem,
 
357
                self.compute['memory_mb_used'])
 
358
        self.assertEqual(5 - claim_mem,
 
359
                self.compute['free_ram_mb'])
 
360
 
 
361
        self.assertEqual(claim_disk,
 
362
                self.compute['local_gb_used'])
 
363
        self.assertEqual(6 - claim_disk,
 
364
                self.compute['free_disk_gb'])
 
365
 
 
366
    def testClaimAndAbort(self):
 
367
        self.assertEqual(5, self.tracker.compute_node['memory_mb'])
 
368
        self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
 
369
 
 
370
        self.assertEqual(6, self.tracker.compute_node['local_gb'])
 
371
        self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
 
372
 
 
373
        claim_mem = 3
 
374
        claim_disk = 2
 
375
        claim = self.tracker.begin_resource_claim(self.context, claim_mem,
 
376
                claim_disk)
 
377
 
 
378
        self.assertEqual(5, self.compute["memory_mb"])
 
379
        self.assertEqual(claim_mem, self.compute["memory_mb_used"])
 
380
        self.assertEqual(5 - claim_mem, self.compute["free_ram_mb"])
 
381
 
 
382
        self.assertEqual(6, self.compute["local_gb"])
 
383
        self.assertEqual(claim_disk, self.compute["local_gb_used"])
 
384
        self.assertEqual(6 - claim_disk, self.compute["free_disk_gb"])
 
385
 
 
386
        self.tracker.abort_resource_claim(self.context, claim)
 
387
 
 
388
        self.assertEqual(5, self.compute["memory_mb"])
 
389
        self.assertEqual(0, self.compute["memory_mb_used"])
 
390
        self.assertEqual(5, self.compute["free_ram_mb"])
 
391
 
 
392
        self.assertEqual(6, self.compute["local_gb"])
 
393
        self.assertEqual(0, self.compute["local_gb_used"])
 
394
        self.assertEqual(6, self.compute["free_disk_gb"])
 
395
 
 
396
    def testExpiredClaims(self):
 
397
        """Test that old claims get cleaned up automatically if not finished
 
398
        or aborted explicitly.
 
399
        """
 
400
        claim = self.tracker.begin_resource_claim(self.context, memory_mb=2,
 
401
                disk_gb=2)
 
402
        claim.expire_ts = timeutils.utcnow_ts() - 1
 
403
        self.assertTrue(claim.is_expired())
 
404
 
 
405
        # and an unexpired claim
 
406
        claim2 = self.tracker.begin_resource_claim(self.context, memory_mb=1,
 
407
                disk_gb=1)
 
408
 
 
409
        self.assertEqual(2, len(self.tracker.claims))
 
410
        self.assertEqual(2 + 1, self.tracker.compute_node['memory_mb_used'])
 
411
        self.assertEqual(2 + 1, self.tracker.compute_node['local_gb_used'])
 
412
 
 
413
        # expired claims get expunged when audit runs:
 
414
        self.tracker.update_available_resource(self.context)
 
415
 
 
416
        self.assertEqual(1, len(self.tracker.claims))
 
417
        self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
 
418
        self.assertEqual(1, self.tracker.compute_node['local_gb_used'])
 
419
 
 
420
        # and just call finish & abort to ensure expired claims do not cause
 
421
        # any other explosions:
 
422
        self.tracker.abort_resource_claim(self.context, claim)
 
423
        self.tracker.finish_resource_claim(claim)
 
424
 
 
425
    def testInstanceClaim(self):
 
426
        self.tracker.begin_instance_resource_claim(self.context,
 
427
                self.instance_ref)
 
428
        self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
 
429
        self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
 
430
 
 
431
    def testContextClaim(self):
 
432
        with self.tracker.resource_claim(self.context, memory_mb=1, disk_gb=1):
 
433
            # <insert exciting things that utilize resources>
 
434
            self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
 
435
            self.assertEqual(1, self.tracker.compute_node['local_gb_used'])
 
436
            self.assertEqual(1, self.compute['memory_mb_used'])
 
437
            self.assertEqual(1, self.compute['local_gb_used'])
 
438
 
 
439
        self.tracker.update_available_resource(self.context)
 
440
        self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
 
441
        self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
 
442
        self.assertEqual(0, self.compute['memory_mb_used'])
 
443
        self.assertEqual(0, self.compute['local_gb_used'])
 
444
 
 
445
    def testContextClaimWithException(self):
 
446
        try:
 
447
            with self.tracker.resource_claim(self.context, memory_mb=1,
 
448
                    disk_gb=1):
 
449
                # <insert exciting things that utilize resources>
 
450
                raise Exception("THE SKY IS FALLING")
 
451
        except Exception:
 
452
            pass
 
453
 
 
454
        self.tracker.update_available_resource(self.context)
 
455
        self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
 
456
        self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
 
457
        self.assertEqual(0, self.compute['memory_mb_used'])
 
458
        self.assertEqual(0, self.compute['local_gb_used'])
 
459
 
 
460
    def testInstanceContextClaim(self):
 
461
        with self.tracker.instance_resource_claim(self.context,
 
462
                self.instance_ref):
 
463
            # <insert exciting things that utilize resources>
 
464
            self.assertEqual(1, self.tracker.compute_node['memory_mb_used'])
 
465
            self.assertEqual(2, self.tracker.compute_node['local_gb_used'])
 
466
            self.assertEqual(1, self.compute['memory_mb_used'])
 
467
            self.assertEqual(2, self.compute['local_gb_used'])
 
468
 
 
469
        self.tracker.update_available_resource(self.context)
 
470
        self.assertEqual(0, self.tracker.compute_node['memory_mb_used'])
 
471
        self.assertEqual(0, self.tracker.compute_node['local_gb_used'])
 
472
        self.assertEqual(0, self.compute['memory_mb_used'])
 
473
        self.assertEqual(0, self.compute['local_gb_used'])
 
474
 
 
475
    def testUpdateLoadStatsForInstance(self):
 
476
        self.assertFalse(self.tracker.disabled)
 
477
        self.assertEqual(0, self.tracker.compute_node['current_workload'])
 
478
 
 
479
        old_ref = self.instance_ref
 
480
        old_ref['task_state'] = task_states.SCHEDULING
 
481
        with self.tracker.instance_resource_claim(self.context, old_ref):
 
482
            pass
 
483
 
 
484
        self.assertEqual(1, self.tracker.compute_node['current_workload'])
 
485
 
 
486
        new_ref = copy.copy(old_ref)
 
487
        new_ref['vm_state'] = vm_states.ACTIVE
 
488
        new_ref['task_state'] = None
 
489
 
 
490
        self.tracker.update_load_stats_for_instance(self.context, old_ref,
 
491
                new_ref)
 
492
        self.assertEqual(0, self.tracker.compute_node['current_workload'])