summaryrefslogtreecommitdiff
path: root/nova/tests/unit/cells/test_cells_weights.py
blob: 5f0a0ac7833f2d8df87786d97c64f2b55a0f0512 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# Copyright (c) 2012 OpenStack Foundation
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
"""
Unit Tests for testing the cells weight algorithms.

Cells with higher weights should be given priority for new builds.
"""

import datetime

from oslo.utils import timeutils

from nova.cells import state
from nova.cells import weights
from nova import test


class FakeCellState(state.CellState):
    def __init__(self, cell_name):
        super(FakeCellState, self).__init__(cell_name)
        self.capacities['ram_free'] = {'total_mb': 0,
                                       'units_by_mb': {}}
        self.db_info = {}

    def _update_ram_free(self, *args):
        ram_free = self.capacities['ram_free']
        for ram_size, units in args:
            ram_free['total_mb'] += units * ram_size
            ram_free['units_by_mb'][str(ram_size)] = units


def _get_fake_cells():

    cell1 = FakeCellState('cell1')
    cell1._update_ram_free((512, 1), (1024, 4), (2048, 3))
    cell1.db_info['weight_offset'] = -200.0
    cell2 = FakeCellState('cell2')
    cell2._update_ram_free((512, 2), (1024, 3), (2048, 4))
    cell2.db_info['weight_offset'] = -200.1
    cell3 = FakeCellState('cell3')
    cell3._update_ram_free((512, 3), (1024, 2), (2048, 1))
    cell3.db_info['weight_offset'] = 400.0
    cell4 = FakeCellState('cell4')
    cell4._update_ram_free((512, 4), (1024, 1), (2048, 2))
    cell4.db_info['weight_offset'] = 300.0

    return [cell1, cell2, cell3, cell4]


class CellsWeightsTestCase(test.NoDBTestCase):
    """Makes sure the proper weighers are in the directory."""

    def test_all_weighers(self):
        weighers = weights.all_weighers()
        # Check at least a couple that we expect are there
        self.assertTrue(len(weighers) >= 2)
        class_names = [cls.__name__ for cls in weighers]
        self.assertIn('WeightOffsetWeigher', class_names)
        self.assertIn('RamByInstanceTypeWeigher', class_names)


class _WeigherTestClass(test.NoDBTestCase):
    """Base class for testing individual weigher plugins."""
    weigher_cls_name = None

    def setUp(self):
        super(_WeigherTestClass, self).setUp()
        self.weight_handler = weights.CellWeightHandler()
        self.weight_classes = self.weight_handler.get_matching_classes(
                [self.weigher_cls_name])

    def _get_weighed_cells(self, cells, weight_properties):
        return self.weight_handler.get_weighed_objects(self.weight_classes,
                cells, weight_properties)


class RAMByInstanceTypeWeigherTestClass(_WeigherTestClass):

    weigher_cls_name = ('nova.cells.weights.ram_by_instance_type.'
                        'RamByInstanceTypeWeigher')

    def test_default_spreading(self):
        """Test that cells with more ram available return a higher weight."""
        cells = _get_fake_cells()
        # Simulate building a new 512MB instance.
        instance_type = {'memory_mb': 512}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[3], cells[2], cells[1], cells[0]]
        self.assertEqual(expected_cells, resulting_cells)

        # Simulate building a new 1024MB instance.
        instance_type = {'memory_mb': 1024}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[0], cells[1], cells[2], cells[3]]
        self.assertEqual(expected_cells, resulting_cells)

        # Simulate building a new 2048MB instance.
        instance_type = {'memory_mb': 2048}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[1], cells[0], cells[3], cells[2]]
        self.assertEqual(expected_cells, resulting_cells)

    def test_negative_multiplier(self):
        """Test that cells with less ram available return a higher weight."""
        self.flags(ram_weight_multiplier=-1.0, group='cells')
        cells = _get_fake_cells()
        # Simulate building a new 512MB instance.
        instance_type = {'memory_mb': 512}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[0], cells[1], cells[2], cells[3]]
        self.assertEqual(expected_cells, resulting_cells)

        # Simulate building a new 1024MB instance.
        instance_type = {'memory_mb': 1024}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[3], cells[2], cells[1], cells[0]]
        self.assertEqual(expected_cells, resulting_cells)

        # Simulate building a new 2048MB instance.
        instance_type = {'memory_mb': 2048}
        weight_properties = {'request_spec': {'instance_type': instance_type}}
        weighed_cells = self._get_weighed_cells(cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        expected_cells = [cells[2], cells[3], cells[0], cells[1]]
        self.assertEqual(expected_cells, resulting_cells)


class WeightOffsetWeigherTestClass(_WeigherTestClass):
    """Test the RAMWeigher class."""
    weigher_cls_name = 'nova.cells.weights.weight_offset.WeightOffsetWeigher'

    def test_weight_offset(self):
        """Test that cells with higher weight_offsets return higher
        weights.
        """
        cells = _get_fake_cells()
        weighed_cells = self._get_weighed_cells(cells, {})
        self.assertEqual(len(weighed_cells), 4)
        expected_cells = [cells[2], cells[3], cells[0], cells[1]]
        resulting_cells = [weighed_cell.obj for weighed_cell in weighed_cells]
        self.assertEqual(expected_cells, resulting_cells)


class MuteWeigherTestClass(_WeigherTestClass):
    weigher_cls_name = 'nova.cells.weights.mute_child.MuteChildWeigher'

    def setUp(self):
        super(MuteWeigherTestClass, self).setUp()
        self.flags(mute_weight_multiplier=-10.0, mute_child_interval=100,
                   mute_weight_value=1000.0, group='cells')

        self.now = timeutils.utcnow()
        timeutils.set_time_override(self.now)

        self.cells = _get_fake_cells()
        for cell in self.cells:
            cell.last_seen = self.now

    def tearDown(self):
        super(MuteWeigherTestClass, self).tearDown()
        timeutils.clear_time_override()

    def test_non_mute(self):
        weight_properties = {}
        weighed_cells = self._get_weighed_cells(self.cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)

        for weighed_cell in weighed_cells:
            self.assertEqual(0, weighed_cell.weight)

    def test_mutes(self):
        # make 2 of them mute:
        self.cells[0].last_seen = (self.cells[0].last_seen -
                                   datetime.timedelta(seconds=200))
        self.cells[1].last_seen = (self.cells[1].last_seen -
                                   datetime.timedelta(seconds=200))

        weight_properties = {}
        weighed_cells = self._get_weighed_cells(self.cells, weight_properties)
        self.assertEqual(len(weighed_cells), 4)

        for i in range(2):
            weighed_cell = weighed_cells.pop(0)
            self.assertEqual(0, weighed_cell.weight)
            self.assertIn(weighed_cell.obj.name, ['cell3', 'cell4'])

        for i in range(2):
            weighed_cell = weighed_cells.pop(0)
            self.assertEqual(-10.0, weighed_cell.weight)
            self.assertIn(weighed_cell.obj.name, ['cell1', 'cell2'])