1#   Copyright 2016 Huawei, Inc. All rights reserved.
2#
3#   Licensed under the Apache License, Version 2.0 (the "License"); you may
4#   not use this file except in compliance with the License. You may obtain
5#   a copy of the License at
6#
7#        http://www.apache.org/licenses/LICENSE-2.0
8#
9#   Unless required by applicable law or agreed to in writing, software
10#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12#   License for the specific language governing permissions and limitations
13#   under the License.
14#
15
16from unittest import mock
17from unittest.mock import call
18
19from osc_lib.cli import format_columns
20from osc_lib import exceptions
21from osc_lib import utils
22
23from openstackclient.compute.v2 import aggregate
24from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
25
26
27class TestAggregate(compute_fakes.TestComputev2):
28
29    fake_ag = compute_fakes.FakeAggregate.create_one_aggregate()
30
31    columns = (
32        'availability_zone',
33        'hosts',
34        'id',
35        'name',
36        'properties',
37    )
38
39    data = (
40        fake_ag.availability_zone,
41        format_columns.ListColumn(fake_ag.hosts),
42        fake_ag.id,
43        fake_ag.name,
44        format_columns.DictColumn(fake_ag.metadata),
45    )
46
47    def setUp(self):
48        super(TestAggregate, self).setUp()
49
50        # Get a shortcut to the AggregateManager Mock
51        self.aggregate_mock = self.app.client_manager.compute.aggregates
52        self.aggregate_mock.reset_mock()
53
54
55class TestAggregateAddHost(TestAggregate):
56
57    def setUp(self):
58        super(TestAggregateAddHost, self).setUp()
59
60        self.aggregate_mock.get.return_value = self.fake_ag
61        self.aggregate_mock.add_host.return_value = self.fake_ag
62        self.cmd = aggregate.AddAggregateHost(self.app, None)
63
64    def test_aggregate_add_host(self):
65        arglist = [
66            'ag1',
67            'host1',
68        ]
69        verifylist = [
70            ('aggregate', 'ag1'),
71            ('host', 'host1'),
72        ]
73        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
74        columns, data = self.cmd.take_action(parsed_args)
75        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
76        self.aggregate_mock.add_host.assert_called_once_with(self.fake_ag,
77                                                             parsed_args.host)
78        self.assertEqual(self.columns, columns)
79        self.assertItemEqual(self.data, data)
80
81
82class TestAggregateCreate(TestAggregate):
83
84    def setUp(self):
85        super(TestAggregateCreate, self).setUp()
86
87        self.aggregate_mock.create.return_value = self.fake_ag
88        self.aggregate_mock.set_metadata.return_value = self.fake_ag
89        self.cmd = aggregate.CreateAggregate(self.app, None)
90
91    def test_aggregate_create(self):
92        arglist = [
93            'ag1',
94        ]
95        verifylist = [
96            ('name', 'ag1'),
97        ]
98        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
99        columns, data = self.cmd.take_action(parsed_args)
100        self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
101                                                           None)
102        self.assertEqual(self.columns, columns)
103        self.assertItemEqual(self.data, data)
104
105    def test_aggregate_create_with_zone(self):
106        arglist = [
107            '--zone', 'zone1',
108            'ag1',
109        ]
110        verifylist = [
111            ('zone', 'zone1'),
112            ('name', 'ag1'),
113        ]
114
115        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
116        columns, data = self.cmd.take_action(parsed_args)
117        self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
118                                                           parsed_args.zone)
119        self.assertEqual(self.columns, columns)
120        self.assertItemEqual(self.data, data)
121
122    def test_aggregate_create_with_property(self):
123        arglist = [
124            '--property', 'key1=value1',
125            '--property', 'key2=value2',
126            'ag1',
127        ]
128        verifylist = [
129            ('property', {'key1': 'value1', 'key2': 'value2'}),
130            ('name', 'ag1'),
131        ]
132        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
133        columns, data = self.cmd.take_action(parsed_args)
134        self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
135                                                           None)
136        self.aggregate_mock.set_metadata.assert_called_once_with(
137            self.fake_ag, parsed_args.property)
138        self.assertEqual(self.columns, columns)
139        self.assertItemEqual(self.data, data)
140
141
142class TestAggregateDelete(TestAggregate):
143
144    fake_ags = compute_fakes.FakeAggregate.create_aggregates(count=2)
145
146    def setUp(self):
147        super(TestAggregateDelete, self).setUp()
148
149        self.aggregate_mock.get = (
150            compute_fakes.FakeAggregate.get_aggregates(self.fake_ags))
151        self.cmd = aggregate.DeleteAggregate(self.app, None)
152
153    def test_aggregate_delete(self):
154        arglist = [
155            self.fake_ags[0].id
156        ]
157        verifylist = [
158            ('aggregate', [self.fake_ags[0].id]),
159        ]
160        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
161        result = self.cmd.take_action(parsed_args)
162        self.aggregate_mock.get.assert_called_once_with(self.fake_ags[0].id)
163        self.aggregate_mock.delete.assert_called_once_with(self.fake_ags[0].id)
164        self.assertIsNone(result)
165
166    def test_delete_multiple_aggregates(self):
167        arglist = []
168        for a in self.fake_ags:
169            arglist.append(a.id)
170        verifylist = [
171            ('aggregate', arglist),
172        ]
173
174        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
175        result = self.cmd.take_action(parsed_args)
176
177        calls = []
178        for a in self.fake_ags:
179            calls.append(call(a.id))
180        self.aggregate_mock.delete.assert_has_calls(calls)
181        self.assertIsNone(result)
182
183    def test_delete_multiple_agggregates_with_exception(self):
184        arglist = [
185            self.fake_ags[0].id,
186            'unexist_aggregate',
187        ]
188        verifylist = [
189            ('aggregate', arglist),
190        ]
191
192        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
193
194        find_mock_result = [self.fake_ags[0], exceptions.CommandError]
195        with mock.patch.object(utils, 'find_resource',
196                               side_effect=find_mock_result) as find_mock:
197            try:
198                self.cmd.take_action(parsed_args)
199                self.fail('CommandError should be raised.')
200            except exceptions.CommandError as e:
201                self.assertEqual('1 of 2 aggregates failed to delete.',
202                                 str(e))
203
204            find_mock.assert_any_call(self.aggregate_mock, self.fake_ags[0].id)
205            find_mock.assert_any_call(self.aggregate_mock, 'unexist_aggregate')
206
207            self.assertEqual(2, find_mock.call_count)
208            self.aggregate_mock.delete.assert_called_once_with(
209                self.fake_ags[0].id
210            )
211
212
213class TestAggregateList(TestAggregate):
214
215    list_columns = (
216        "ID",
217        "Name",
218        "Availability Zone",
219    )
220
221    list_columns_long = (
222        "ID",
223        "Name",
224        "Availability Zone",
225        "Properties",
226    )
227
228    list_data = ((
229        TestAggregate.fake_ag.id,
230        TestAggregate.fake_ag.name,
231        TestAggregate.fake_ag.availability_zone,
232    ), )
233
234    list_data_long = ((
235        TestAggregate.fake_ag.id,
236        TestAggregate.fake_ag.name,
237        TestAggregate.fake_ag.availability_zone,
238        format_columns.DictColumn({
239            key: value
240            for key, value in TestAggregate.fake_ag.metadata.items()
241            if key != 'availability_zone'
242        }),
243    ), )
244
245    def setUp(self):
246        super(TestAggregateList, self).setUp()
247
248        self.aggregate_mock.list.return_value = [self.fake_ag]
249        self.cmd = aggregate.ListAggregate(self.app, None)
250
251    def test_aggregate_list(self):
252
253        parsed_args = self.check_parser(self.cmd, [], [])
254        columns, data = self.cmd.take_action(parsed_args)
255
256        self.assertEqual(self.list_columns, columns)
257        self.assertItemEqual(self.list_data, tuple(data))
258
259    def test_aggregate_list_with_long(self):
260        arglist = [
261            '--long',
262        ]
263        vertifylist = [
264            ('long', True),
265        ]
266        parsed_args = self.check_parser(self.cmd, arglist, vertifylist)
267        columns, data = self.cmd.take_action(parsed_args)
268
269        self.assertEqual(self.list_columns_long, columns)
270        self.assertListItemEqual(self.list_data_long, tuple(data))
271
272
273class TestAggregateRemoveHost(TestAggregate):
274
275    def setUp(self):
276        super(TestAggregateRemoveHost, self).setUp()
277
278        self.aggregate_mock.get.return_value = self.fake_ag
279        self.aggregate_mock.remove_host.return_value = self.fake_ag
280        self.cmd = aggregate.RemoveAggregateHost(self.app, None)
281
282    def test_aggregate_add_host(self):
283        arglist = [
284            'ag1',
285            'host1',
286        ]
287        verifylist = [
288            ('aggregate', 'ag1'),
289            ('host', 'host1'),
290        ]
291        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
292        columns, data = self.cmd.take_action(parsed_args)
293        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
294        self.aggregate_mock.remove_host.assert_called_once_with(
295            self.fake_ag, parsed_args.host)
296        self.assertEqual(self.columns, columns)
297        self.assertItemEqual(self.data, data)
298
299
300class TestAggregateSet(TestAggregate):
301
302    def setUp(self):
303        super(TestAggregateSet, self).setUp()
304
305        self.aggregate_mock.get.return_value = self.fake_ag
306        self.cmd = aggregate.SetAggregate(self.app, None)
307
308    def test_aggregate_set_no_option(self):
309        arglist = [
310            'ag1',
311        ]
312        verifylist = [
313            ('aggregate', 'ag1'),
314        ]
315        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
316        result = self.cmd.take_action(parsed_args)
317
318        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
319        self.assertNotCalled(self.aggregate_mock.update)
320        self.assertNotCalled(self.aggregate_mock.set_metadata)
321        self.assertIsNone(result)
322
323    def test_aggregate_set_with_name(self):
324        arglist = [
325            '--name', 'new_name',
326            'ag1',
327        ]
328        verifylist = [
329            ('name', 'new_name'),
330            ('aggregate', 'ag1'),
331        ]
332        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
333        result = self.cmd.take_action(parsed_args)
334
335        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
336        self.aggregate_mock.update.assert_called_once_with(
337            self.fake_ag, {'name': parsed_args.name})
338        self.assertNotCalled(self.aggregate_mock.set_metadata)
339        self.assertIsNone(result)
340
341    def test_aggregate_set_with_zone(self):
342        arglist = [
343            '--zone', 'new_zone',
344            'ag1',
345        ]
346        verifylist = [
347            ('zone', 'new_zone'),
348            ('aggregate', 'ag1'),
349        ]
350        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
351        result = self.cmd.take_action(parsed_args)
352
353        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
354        self.aggregate_mock.update.assert_called_once_with(
355            self.fake_ag, {'availability_zone': parsed_args.zone})
356        self.assertNotCalled(self.aggregate_mock.set_metadata)
357        self.assertIsNone(result)
358
359    def test_aggregate_set_with_property(self):
360        arglist = [
361            '--property', 'key1=value1',
362            '--property', 'key2=value2',
363            'ag1',
364        ]
365        verifylist = [
366            ('property', {'key1': 'value1', 'key2': 'value2'}),
367            ('aggregate', 'ag1'),
368        ]
369        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
370        result = self.cmd.take_action(parsed_args)
371
372        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
373        self.assertNotCalled(self.aggregate_mock.update)
374        self.aggregate_mock.set_metadata.assert_called_once_with(
375            self.fake_ag, parsed_args.property)
376        self.assertIsNone(result)
377
378    def test_aggregate_set_with_no_property_and_property(self):
379        arglist = [
380            '--no-property',
381            '--property', 'key2=value2',
382            'ag1',
383        ]
384        verifylist = [
385            ('no_property', True),
386            ('property', {'key2': 'value2'}),
387            ('aggregate', 'ag1'),
388        ]
389        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
390        result = self.cmd.take_action(parsed_args)
391        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
392        self.assertNotCalled(self.aggregate_mock.update)
393        self.aggregate_mock.set_metadata.assert_called_once_with(
394            self.fake_ag, {'key1': None, 'key2': 'value2'})
395        self.assertIsNone(result)
396
397    def test_aggregate_set_with_no_property(self):
398        arglist = [
399            '--no-property',
400            'ag1',
401        ]
402        verifylist = [
403            ('no_property', True),
404            ('aggregate', 'ag1'),
405        ]
406        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
407        result = self.cmd.take_action(parsed_args)
408        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
409        self.assertNotCalled(self.aggregate_mock.update)
410        self.aggregate_mock.set_metadata.assert_called_once_with(
411            self.fake_ag, {'key1': None})
412        self.assertIsNone(result)
413
414    def test_aggregate_set_with_zone_and_no_property(self):
415        arglist = [
416            '--zone', 'new_zone',
417            '--no-property',
418            'ag1',
419        ]
420        verifylist = [
421            ('zone', 'new_zone'),
422            ('no_property', True),
423            ('aggregate', 'ag1'),
424        ]
425        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
426        result = self.cmd.take_action(parsed_args)
427        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
428        self.aggregate_mock.update.assert_called_once_with(
429            self.fake_ag, {'availability_zone': parsed_args.zone})
430        self.aggregate_mock.set_metadata.assert_called_once_with(
431            self.fake_ag, {'key1': None})
432        self.assertIsNone(result)
433
434
435class TestAggregateShow(TestAggregate):
436
437    columns = (
438        'availability_zone',
439        'hosts',
440        'id',
441        'name',
442        'properties',
443    )
444
445    data = (
446        TestAggregate.fake_ag.availability_zone,
447        format_columns.ListColumn(TestAggregate.fake_ag.hosts),
448        TestAggregate.fake_ag.id,
449        TestAggregate.fake_ag.name,
450        format_columns.DictColumn({
451            key: value
452            for key, value in TestAggregate.fake_ag.metadata.items()
453            if key != 'availability_zone'
454        }),
455    )
456
457    def setUp(self):
458        super(TestAggregateShow, self).setUp()
459
460        self.aggregate_mock.get.return_value = self.fake_ag
461        self.cmd = aggregate.ShowAggregate(self.app, None)
462
463    def test_aggregate_show(self):
464        arglist = [
465            'ag1',
466        ]
467        verifylist = [
468            ('aggregate', 'ag1'),
469        ]
470        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
471        columns, data = self.cmd.take_action(parsed_args)
472        self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
473
474        self.assertEqual(self.columns, columns)
475        self.assertItemEqual(self.data, tuple(data))
476
477
478class TestAggregateUnset(TestAggregate):
479
480    def setUp(self):
481        super(TestAggregateUnset, self).setUp()
482
483        self.aggregate_mock.get.return_value = self.fake_ag
484        self.cmd = aggregate.UnsetAggregate(self.app, None)
485
486    def test_aggregate_unset(self):
487        arglist = [
488            '--property', 'unset_key',
489            'ag1',
490        ]
491        verifylist = [
492            ('property', ['unset_key']),
493            ('aggregate', 'ag1'),
494        ]
495
496        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
497        result = self.cmd.take_action(parsed_args)
498        self.aggregate_mock.set_metadata.assert_called_once_with(
499            self.fake_ag, {'unset_key': None})
500        self.assertIsNone(result)
501
502    def test_aggregate_unset_multiple_properties(self):
503        arglist = [
504            '--property', 'unset_key1',
505            '--property', 'unset_key2',
506            'ag1',
507        ]
508        verifylist = [
509            ('property', ['unset_key1', 'unset_key2']),
510            ('aggregate', 'ag1'),
511        ]
512
513        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
514        result = self.cmd.take_action(parsed_args)
515        self.aggregate_mock.set_metadata.assert_called_once_with(
516            self.fake_ag, {'unset_key1': None, 'unset_key2': None})
517        self.assertIsNone(result)
518
519    def test_aggregate_unset_no_option(self):
520        arglist = [
521            'ag1',
522        ]
523        verifylist = [
524            ('property', None),
525            ('aggregate', 'ag1'),
526        ]
527        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
528        result = self.cmd.take_action(parsed_args)
529        self.assertNotCalled(self.aggregate_mock.set_metadata)
530        self.assertIsNone(result)
531