1#   Copyright 2013 Nebula Inc.
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
16import copy
17from unittest import mock
18import uuid
19
20from novaclient import api_versions
21
22from openstackclient.api import compute_v2
23from openstackclient.tests.unit import fakes
24from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
25from openstackclient.tests.unit.image.v2 import fakes as image_fakes
26from openstackclient.tests.unit.network.v2 import fakes as network_fakes
27from openstackclient.tests.unit import utils
28from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
29
30floating_ip_num = 100
31fix_ip_num = 100
32injected_file_num = 100
33injected_file_size_num = 10240
34injected_path_size_num = 255
35key_pair_num = 100
36core_num = 20
37ram_num = 51200
38instance_num = 10
39property_num = 128
40secgroup_rule_num = 20
41secgroup_num = 10
42servgroup_num = 10
43servgroup_members_num = 10
44project_name = 'project_test'
45QUOTA = {
46    'project': project_name,
47    'floating-ips': floating_ip_num,
48    'fix-ips': fix_ip_num,
49    'injected-files': injected_file_num,
50    'injected-file-size': injected_file_size_num,
51    'injected-path-size': injected_path_size_num,
52    'key-pairs': key_pair_num,
53    'cores': core_num,
54    'ram': ram_num,
55    'instances': instance_num,
56    'properties': property_num,
57    'secgroup_rules': secgroup_rule_num,
58    'secgroups': secgroup_num,
59    'server-groups': servgroup_num,
60    'server-group-members': servgroup_members_num
61}
62
63QUOTA_columns = tuple(sorted(QUOTA))
64QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA))
65
66
67class FakeAggregate(object):
68    """Fake one aggregate."""
69
70    @staticmethod
71    def create_one_aggregate(attrs=None):
72        """Create a fake aggregate.
73
74        :param Dictionary attrs:
75            A dictionary with all attributes
76        :return:
77            A FakeResource object, with id and other attributes
78        """
79        attrs = attrs or {}
80
81        # Set default attribute
82        aggregate_info = {
83            "name": "aggregate-name-" + uuid.uuid4().hex,
84            "availability_zone": "ag_zone",
85            "hosts": [],
86            "id": "aggregate-id-" + uuid.uuid4().hex,
87            "metadata": {
88                "availability_zone": "ag_zone",
89                "key1": "value1",
90            }
91        }
92
93        # Overwrite default attributes.
94        aggregate_info.update(attrs)
95
96        aggregate = fakes.FakeResource(
97            info=copy.deepcopy(aggregate_info),
98            loaded=True)
99        return aggregate
100
101    @staticmethod
102    def create_aggregates(attrs=None, count=2):
103        """Create multiple fake aggregates.
104
105        :param Dictionary attrs:
106            A dictionary with all attributes
107        :param int count:
108            The number of aggregates to fake
109        :return:
110            A list of FakeResource objects faking the aggregates
111        """
112        aggregates = []
113        for i in range(0, count):
114            aggregates.append(FakeAggregate.create_one_aggregate(attrs))
115
116        return aggregates
117
118    @staticmethod
119    def get_aggregates(aggregates=None, count=2):
120        """Get an iterable MagicMock object with a list of faked aggregates.
121
122        If aggregates list is provided, then initialize the Mock object
123        with the list. Otherwise create one.
124
125        :param List aggregates:
126            A list of FakeResource objects faking aggregates
127        :param int count:
128            The number of aggregates to fake
129        :return:
130            An iterable Mock object with side_effect set to a list of faked
131            aggregates
132        """
133        if aggregates is None:
134            aggregates = FakeAggregate.create_aggregates(count)
135        return mock.Mock(side_effect=aggregates)
136
137
138class FakeComputev2Client(object):
139
140    def __init__(self, **kwargs):
141        self.agents = mock.Mock()
142        self.agents.resource_class = fakes.FakeResource(None, {})
143
144        self.aggregates = mock.Mock()
145        self.aggregates.resource_class = fakes.FakeResource(None, {})
146
147        self.availability_zones = mock.Mock()
148        self.availability_zones.resource_class = fakes.FakeResource(None, {})
149
150        self.images = mock.Mock()
151        self.images.resource_class = fakes.FakeResource(None, {})
152
153        self.limits = mock.Mock()
154        self.limits.resource_class = fakes.FakeResource(None, {})
155
156        self.servers = mock.Mock()
157        self.servers.resource_class = fakes.FakeResource(None, {})
158
159        self.services = mock.Mock()
160        self.services.resource_class = fakes.FakeResource(None, {})
161
162        self.extensions = mock.Mock()
163        self.extensions.resource_class = fakes.FakeResource(None, {})
164
165        self.flavors = mock.Mock()
166        self.flavors.resource_class = fakes.FakeResource(None, {})
167
168        self.flavor_access = mock.Mock()
169        self.flavor_access.resource_class = fakes.FakeResource(None, {})
170
171        self.quotas = mock.Mock()
172        self.quotas.resource_class = fakes.FakeResource(None, {})
173
174        self.quota_classes = mock.Mock()
175        self.quota_classes.resource_class = fakes.FakeResource(None, {})
176
177        self.usage = mock.Mock()
178        self.usage.resource_class = fakes.FakeResource(None, {})
179
180        self.volumes = mock.Mock()
181        self.volumes.resource_class = fakes.FakeResource(None, {})
182
183        self.hypervisors = mock.Mock()
184        self.hypervisors.resource_class = fakes.FakeResource(None, {})
185
186        self.hypervisors_stats = mock.Mock()
187        self.hypervisors_stats.resource_class = fakes.FakeResource(None, {})
188
189        self.keypairs = mock.Mock()
190        self.keypairs.resource_class = fakes.FakeResource(None, {})
191
192        self.hosts = mock.Mock()
193        self.hosts.resource_class = fakes.FakeResource(None, {})
194
195        self.server_groups = mock.Mock()
196        self.server_groups.resource_class = fakes.FakeResource(None, {})
197
198        self.instance_action = mock.Mock()
199        self.instance_action.resource_class = fakes.FakeResource(None, {})
200
201        self.auth_token = kwargs['token']
202
203        self.management_url = kwargs['endpoint']
204
205        self.api_version = api_versions.APIVersion('2.1')
206
207
208class TestComputev2(utils.TestCommand):
209
210    def setUp(self):
211        super(TestComputev2, self).setUp()
212
213        self.app.client_manager.compute = FakeComputev2Client(
214            endpoint=fakes.AUTH_URL,
215            token=fakes.AUTH_TOKEN,
216        )
217
218        self.app.client_manager.compute.api = compute_v2.APIv2(
219            session=self.app.client_manager.session,
220            endpoint=fakes.AUTH_URL,
221        )
222
223        self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client(
224            endpoint=fakes.AUTH_URL,
225            token=fakes.AUTH_TOKEN,
226        )
227
228        self.app.client_manager.image = image_fakes.FakeImagev2Client(
229            endpoint=fakes.AUTH_URL,
230            token=fakes.AUTH_TOKEN,
231        )
232
233        self.app.client_manager.network = network_fakes.FakeNetworkV2Client(
234            endpoint=fakes.AUTH_URL,
235            token=fakes.AUTH_TOKEN,
236        )
237
238        self.app.client_manager.volume = volume_fakes.FakeVolumeClient(
239            endpoint=fakes.AUTH_URL,
240            token=fakes.AUTH_TOKEN,
241        )
242
243
244class FakeAgent(object):
245    """Fake one or more agent."""
246
247    @staticmethod
248    def create_one_agent(attrs=None):
249        """Create a fake agent.
250
251        :param Dictionary attrs:
252            A dictionary with all attributes
253        :return:
254            A FakeResource object, with agent_id, os, and so on
255        """
256
257        attrs = attrs or {}
258
259        # set default attributes.
260        agent_info = {
261            'agent_id': 'agent-id-' + uuid.uuid4().hex,
262            'os': 'agent-os-' + uuid.uuid4().hex,
263            'architecture': 'agent-architecture',
264            'version': '8.0',
265            'url': 'http://127.0.0.1',
266            'md5hash': 'agent-md5hash',
267            'hypervisor': 'hypervisor',
268        }
269
270        # Overwrite default attributes.
271        agent_info.update(attrs)
272
273        agent = fakes.FakeResource(info=copy.deepcopy(agent_info),
274                                   loaded=True)
275        return agent
276
277    @staticmethod
278    def create_agents(attrs=None, count=2):
279        """Create multiple fake agents.
280
281        :param Dictionary attrs:
282            A dictionary with all attributes
283        :param int count:
284            The number of agents to fake
285        :return:
286            A list of FakeResource objects faking the agents
287        """
288        agents = []
289        for i in range(0, count):
290            agents.append(FakeAgent.create_one_agent(attrs))
291
292        return agents
293
294
295class FakeExtension(object):
296    """Fake one or more extension."""
297
298    @staticmethod
299    def create_one_extension(attrs=None):
300        """Create a fake extension.
301
302        :param Dictionary attrs:
303            A dictionary with all attributes
304        :return:
305            A FakeResource object with name, namespace, etc.
306        """
307        attrs = attrs or {}
308
309        # Set default attributes.
310        extension_info = {
311            'name': 'name-' + uuid.uuid4().hex,
312            'namespace': (
313                'http://docs.openstack.org/compute/ext/multinic/api/v1.1'),
314            'description': 'description-' + uuid.uuid4().hex,
315            'updated': '2014-01-07T12:00:0-00:00',
316            'alias': 'NMN',
317            'links': ('[{"href":'
318                      '"https://github.com/openstack/compute-api", "type":'
319                      ' "text/html", "rel": "describedby"}]')
320        }
321
322        # Overwrite default attributes.
323        extension_info.update(attrs)
324
325        extension = fakes.FakeResource(
326            info=copy.deepcopy(extension_info),
327            loaded=True)
328        return extension
329
330
331class FakeHypervisor(object):
332    """Fake one or more hypervisor."""
333
334    @staticmethod
335    def create_one_hypervisor(attrs=None):
336        """Create a fake hypervisor.
337
338        :param Dictionary attrs:
339            A dictionary with all attributes
340        :return:
341            A FakeResource object, with id, hypervisor_hostname, and so on
342        """
343        attrs = attrs or {}
344
345        # Set default attributes.
346        hypervisor_info = {
347            'id': 'hypervisor-id-' + uuid.uuid4().hex,
348            'hypervisor_hostname': 'hypervisor-hostname-' + uuid.uuid4().hex,
349            'status': 'enabled',
350            'host_ip': '192.168.0.10',
351            'cpu_info': {
352                'aaa': 'aaa',
353            },
354            'free_disk_gb': 50,
355            'hypervisor_version': 2004001,
356            'disk_available_least': 50,
357            'local_gb': 50,
358            'free_ram_mb': 1024,
359            'service': {
360                'host': 'aaa',
361                'disabled_reason': None,
362                'id': 1,
363            },
364            'vcpus_used': 0,
365            'hypervisor_type': 'QEMU',
366            'local_gb_used': 0,
367            'vcpus': 4,
368            'memory_mb_used': 512,
369            'memory_mb': 1024,
370            'current_workload': 0,
371            'state': 'up',
372            'running_vms': 0,
373        }
374
375        # Overwrite default attributes.
376        hypervisor_info.update(attrs)
377
378        hypervisor = fakes.FakeResource(info=copy.deepcopy(hypervisor_info),
379                                        loaded=True)
380        return hypervisor
381
382    @staticmethod
383    def create_hypervisors(attrs=None, count=2):
384        """Create multiple fake hypervisors.
385
386        :param Dictionary attrs:
387            A dictionary with all attributes
388        :param int count:
389            The number of hypervisors to fake
390        :return:
391            A list of FakeResource objects faking the hypervisors
392        """
393        hypervisors = []
394        for i in range(0, count):
395            hypervisors.append(FakeHypervisor.create_one_hypervisor(attrs))
396
397        return hypervisors
398
399
400class FakeHypervisorStats(object):
401    """Fake one or more hypervisor stats."""
402
403    @staticmethod
404    def create_one_hypervisor_stats(attrs=None):
405        """Create a fake hypervisor stats.
406
407        :param Dictionary attrs:
408            A dictionary with all attributes
409        :return:
410            A FakeResource object, with count, current_workload, and so on
411        """
412        attrs = attrs or {}
413
414        # Set default attributes.
415        stats_info = {
416            'count': 2,
417            'current_workload': 0,
418            'disk_available_least': 50,
419            'free_disk_gb': 100,
420            'free_ram_mb': 23000,
421            'local_gb': 100,
422            'local_gb_used': 0,
423            'memory_mb': 23800,
424            'memory_mb_used': 1400,
425            'running_vms': 3,
426            'vcpus': 8,
427            'vcpus_used': 3,
428        }
429
430        # Overwrite default attributes.
431        stats_info.update(attrs)
432
433        # Set default method.
434        hypervisor_stats_method = {'to_dict': stats_info}
435
436        hypervisor_stats = fakes.FakeResource(
437            info=copy.deepcopy(stats_info),
438            methods=copy.deepcopy(hypervisor_stats_method),
439            loaded=True)
440        return hypervisor_stats
441
442    @staticmethod
443    def create_hypervisors_stats(attrs=None, count=2):
444        """Create multiple fake hypervisors stats.
445
446        :param Dictionary attrs:
447            A dictionary with all attributes
448        :param int count:
449            The number of hypervisors to fake
450        :return:
451            A list of FakeResource objects faking the hypervisors
452        """
453        hypervisors = []
454        for i in range(0, count):
455            hypervisors.append(
456                FakeHypervisorStats.create_one_hypervisor_stats(attrs))
457
458        return hypervisors
459
460
461class FakeSecurityGroup(object):
462    """Fake one or more security groups."""
463
464    @staticmethod
465    def create_one_security_group(attrs=None):
466        """Create a fake security group.
467
468        :param Dictionary attrs:
469            A dictionary with all attributes
470        :return:
471            A FakeResource object, with id, name, etc.
472        """
473        attrs = attrs or {}
474
475        # Set default attributes.
476        security_group_attrs = {
477            'id': 'security-group-id-' + uuid.uuid4().hex,
478            'name': 'security-group-name-' + uuid.uuid4().hex,
479            'description': 'security-group-description-' + uuid.uuid4().hex,
480            'tenant_id': 'project-id-' + uuid.uuid4().hex,
481            'rules': [],
482        }
483
484        # Overwrite default attributes.
485        security_group_attrs.update(attrs)
486        return security_group_attrs
487
488    @staticmethod
489    def create_security_groups(attrs=None, count=2):
490        """Create multiple fake security groups.
491
492        :param Dictionary attrs:
493            A dictionary with all attributes
494        :param int count:
495            The number of security groups to fake
496        :return:
497            A list of FakeResource objects faking the security groups
498        """
499        security_groups = []
500        for i in range(0, count):
501            security_groups.append(
502                FakeSecurityGroup.create_one_security_group(attrs))
503
504        return security_groups
505
506    @staticmethod
507    def get_security_groups(security_groups=None, count=2):
508        """Get an iterable MagicMock with a list of faked security groups.
509
510        If security groups list is provided, then initialize the Mock object
511        with the list. Otherwise create one.
512
513        :param List security_groups:
514            A list of FakeResource objects faking security groups
515        :param int count:
516            The number of security groups to fake
517        :return:
518            An iterable Mock object with side_effect set to a list of faked
519            security groups
520        """
521        if security_groups is None:
522            security_groups = FakeSecurityGroup.create_security_groups(count)
523        return mock.Mock(side_effect=security_groups)
524
525
526class FakeSecurityGroupRule(object):
527    """Fake one or more security group rules."""
528
529    @staticmethod
530    def create_one_security_group_rule(attrs=None):
531        """Create a fake security group rule.
532
533        :param Dictionary attrs:
534            A dictionary with all attributes
535        :return:
536            A FakeResource object, with id, etc.
537        """
538        attrs = attrs or {}
539
540        # Set default attributes.
541        security_group_rule_attrs = {
542            'from_port': 0,
543            'group': {},
544            'id': 'security-group-rule-id-' + uuid.uuid4().hex,
545            'ip_protocol': 'tcp',
546            'ip_range': {'cidr': '0.0.0.0/0'},
547            'parent_group_id': 'security-group-id-' + uuid.uuid4().hex,
548            'to_port': 0,
549        }
550
551        # Overwrite default attributes.
552        security_group_rule_attrs.update(attrs)
553
554        return security_group_rule_attrs
555
556    @staticmethod
557    def create_security_group_rules(attrs=None, count=2):
558        """Create multiple fake security group rules.
559
560        :param Dictionary attrs:
561            A dictionary with all attributes
562        :param int count:
563            The number of security group rules to fake
564        :return:
565            A list of FakeResource objects faking the security group rules
566        """
567        security_group_rules = []
568        for i in range(0, count):
569            security_group_rules.append(
570                FakeSecurityGroupRule.create_one_security_group_rule(attrs))
571
572        return security_group_rules
573
574
575class FakeServer(object):
576    """Fake one or more compute servers."""
577
578    @staticmethod
579    def create_one_server(attrs=None, methods=None):
580        """Create a fake server.
581
582        :param Dictionary attrs:
583            A dictionary with all attributes
584        :param Dictionary methods:
585            A dictionary with all methods
586        :return:
587            A FakeResource object, with id, name, metadata, and so on
588        """
589        attrs = attrs or {}
590        methods = methods or {}
591
592        # Set default attributes.
593        server_info = {
594            'id': 'server-id-' + uuid.uuid4().hex,
595            'name': 'server-name-' + uuid.uuid4().hex,
596            'metadata': {},
597            'image': {
598                'id': 'image-id-' + uuid.uuid4().hex,
599            },
600            'flavor': {
601                'id': 'flavor-id-' + uuid.uuid4().hex,
602            },
603            'OS-EXT-STS:power_state': 1,
604        }
605
606        # Overwrite default attributes.
607        server_info.update(attrs)
608
609        server = fakes.FakeResource(info=copy.deepcopy(server_info),
610                                    methods=methods,
611                                    loaded=True)
612        return server
613
614    @staticmethod
615    def create_servers(attrs=None, methods=None, count=2):
616        """Create multiple fake servers.
617
618        :param Dictionary attrs:
619            A dictionary with all attributes
620        :param Dictionary methods:
621            A dictionary with all methods
622        :param int count:
623            The number of servers to fake
624        :return:
625            A list of FakeResource objects faking the servers
626        """
627        servers = []
628        for i in range(0, count):
629            servers.append(FakeServer.create_one_server(attrs, methods))
630
631        return servers
632
633    @staticmethod
634    def get_servers(servers=None, count=2):
635        """Get an iterable MagicMock object with a list of faked servers.
636
637        If servers list is provided, then initialize the Mock object with the
638        list. Otherwise create one.
639
640        :param List servers:
641            A list of FakeResource objects faking servers
642        :param int count:
643            The number of servers to fake
644        :return:
645            An iterable Mock object with side_effect set to a list of faked
646            servers
647        """
648        if servers is None:
649            servers = FakeServer.create_servers(count)
650        return mock.Mock(side_effect=servers)
651
652
653class FakeServerEvent(object):
654    """Fake one or more server event."""
655
656    @staticmethod
657    def create_one_server_event(attrs=None):
658        """Create a fake server event.
659
660        :param attrs:
661            A dictionary with all attributes
662        :return:
663            A FakeResource object, with id and other attributes
664        """
665        attrs = attrs or {}
666
667        # Set default attributes
668        server_event_info = {
669            "instance_uuid": "server-event-" + uuid.uuid4().hex,
670            "user_id": "user-id-" + uuid.uuid4().hex,
671            "start_time": "2017-02-27T07:47:13.000000",
672            "request_id": "req-" + uuid.uuid4().hex,
673            "action": "create",
674            "message": None,
675            "project_id": "project-id-" + uuid.uuid4().hex,
676            "events": [{
677                "finish_time": "2017-02-27T07:47:25.000000",
678                "start_time": "2017-02-27T07:47:15.000000",
679                "traceback": None,
680                "event": "compute__do_build_and_run_instance",
681                "result": "Success"
682            }]
683        }
684        # Overwrite default attributes
685        server_event_info.update(attrs)
686
687        server_event = fakes.FakeResource(
688            info=copy.deepcopy(server_event_info),
689            loaded=True,
690        )
691        return server_event
692
693
694class FakeService(object):
695    """Fake one or more services."""
696
697    @staticmethod
698    def create_one_service(attrs=None):
699        """Create a fake service.
700
701        :param Dictionary attrs:
702            A dictionary with all attributes
703        :return:
704            A FakeResource object, with id, host, binary, and so on
705        """
706        attrs = attrs or {}
707
708        # Set default attributes.
709        service_info = {
710            'id': 'id-' + uuid.uuid4().hex,
711            'host': 'host-' + uuid.uuid4().hex,
712            'binary': 'binary-' + uuid.uuid4().hex,
713            'status': 'enabled',
714            'zone': 'zone-' + uuid.uuid4().hex,
715            'state': 'state-' + uuid.uuid4().hex,
716            'updated_at': 'time-' + uuid.uuid4().hex,
717            'disabled_reason': 'earthquake',
718        }
719
720        # Overwrite default attributes.
721        service_info.update(attrs)
722
723        service = fakes.FakeResource(info=copy.deepcopy(service_info),
724                                     loaded=True)
725
726        return service
727
728    @staticmethod
729    def create_services(attrs=None, count=2):
730        """Create multiple fake services.
731
732        :param Dictionary attrs:
733            A dictionary with all attributes
734        :param int count:
735            The number of services to fake
736        :return:
737            A list of FakeResource objects faking the services
738        """
739        services = []
740        for i in range(0, count):
741            services.append(FakeService.create_one_service(attrs))
742
743        return services
744
745
746class FakeFlavor(object):
747    """Fake one or more flavors."""
748
749    @staticmethod
750    def create_one_flavor(attrs=None):
751        """Create a fake flavor.
752
753        :param Dictionary attrs:
754            A dictionary with all attributes
755        :return:
756            A FakeResource object, with id, name, ram, vcpus, and so on
757        """
758        attrs = attrs or {}
759
760        # Set default attributes.
761        flavor_info = {
762            'id': 'flavor-id-' + uuid.uuid4().hex,
763            'name': 'flavor-name-' + uuid.uuid4().hex,
764            'ram': 8192,
765            'vcpus': 4,
766            'disk': 128,
767            'swap': 0,
768            'rxtx_factor': 1.0,
769            'OS-FLV-DISABLED:disabled': False,
770            'os-flavor-access:is_public': True,
771            'description': 'description',
772            'OS-FLV-EXT-DATA:ephemeral': 0,
773            'properties': {'property': 'value'},
774        }
775
776        # Overwrite default attributes.
777        flavor_info.update(attrs)
778
779        # Set default methods.
780        flavor_methods = {
781            'set_keys': None,
782            'unset_keys': None,
783            'get_keys': {'property': 'value'},
784        }
785
786        flavor = fakes.FakeResource(info=copy.deepcopy(flavor_info),
787                                    methods=flavor_methods,
788                                    loaded=True)
789
790        # Set attributes with special mappings in nova client.
791        flavor.disabled = flavor_info['OS-FLV-DISABLED:disabled']
792        flavor.is_public = flavor_info['os-flavor-access:is_public']
793        flavor.ephemeral = flavor_info['OS-FLV-EXT-DATA:ephemeral']
794
795        return flavor
796
797    @staticmethod
798    def create_flavors(attrs=None, count=2):
799        """Create multiple fake flavors.
800
801        :param Dictionary attrs:
802            A dictionary with all attributes
803        :param int count:
804            The number of flavors to fake
805        :return:
806            A list of FakeResource objects faking the flavors
807        """
808        flavors = []
809        for i in range(0, count):
810            flavors.append(FakeFlavor.create_one_flavor(attrs))
811
812        return flavors
813
814    @staticmethod
815    def get_flavors(flavors=None, count=2):
816        """Get an iterable MagicMock object with a list of faked flavors.
817
818        If flavors list is provided, then initialize the Mock object with the
819        list. Otherwise create one.
820
821        :param List flavors:
822            A list of FakeResource objects faking flavors
823        :param int count:
824            The number of flavors to fake
825        :return:
826            An iterable Mock object with side_effect set to a list of faked
827            flavors
828        """
829        if flavors is None:
830            flavors = FakeFlavor.create_flavors(count)
831        return mock.Mock(side_effect=flavors)
832
833
834class FakeFlavorAccess(object):
835    """Fake one or more flavor accesses."""
836
837    @staticmethod
838    def create_one_flavor_access(attrs=None):
839        """Create a fake flavor access.
840
841        :param Dictionary attrs:
842            A dictionary with all attributes
843        :return:
844            A FakeResource object, with flavor_id, tenat_id
845        """
846        attrs = attrs or {}
847
848        # Set default attributes.
849        flavor_access_info = {
850            'flavor_id': 'flavor-id-' + uuid.uuid4().hex,
851            'tenant_id': 'tenant-id-' + uuid.uuid4().hex,
852        }
853
854        # Overwrite default attributes.
855        flavor_access_info.update(attrs)
856
857        flavor_access = fakes.FakeResource(
858            info=copy.deepcopy(flavor_access_info), loaded=True)
859
860        return flavor_access
861
862
863class FakeKeypair(object):
864    """Fake one or more keypairs."""
865
866    @staticmethod
867    def create_one_keypair(attrs=None, no_pri=False):
868        """Create a fake keypair
869
870        :param Dictionary attrs:
871            A dictionary with all attributes
872        :return:
873            A FakeResource object, name, fingerprint, and so on
874        """
875        attrs = attrs or {}
876
877        # Set default attributes.
878        keypair_info = {
879            'name': 'keypair-name-' + uuid.uuid4().hex,
880            'fingerprint': 'dummy',
881            'public_key': 'dummy',
882            'user_id': 'user'
883        }
884        if not no_pri:
885            keypair_info['private_key'] = 'private_key'
886
887        # Overwrite default attributes.
888        keypair_info.update(attrs)
889
890        keypair = fakes.FakeResource(info=copy.deepcopy(keypair_info),
891                                     loaded=True)
892
893        return keypair
894
895    @staticmethod
896    def create_keypairs(attrs=None, count=2):
897        """Create multiple fake keypairs.
898
899        :param Dictionary attrs:
900            A dictionary with all attributes
901        :param int count:
902            The number of keypairs to fake
903        :return:
904            A list of FakeResource objects faking the keypairs
905        """
906
907        keypairs = []
908        for i in range(0, count):
909            keypairs.append(FakeKeypair.create_one_keypair(attrs))
910
911        return keypairs
912
913    @staticmethod
914    def get_keypairs(keypairs=None, count=2):
915        """Get an iterable MagicMock object with a list of faked keypairs.
916
917        If keypairs list is provided, then initialize the Mock object with the
918        list. Otherwise create one.
919
920        :param List keypairs:
921            A list of FakeResource objects faking keypairs
922        :param int count:
923            The number of keypairs to fake
924        :return:
925            An iterable Mock object with side_effect set to a list of faked
926            keypairs
927        """
928        if keypairs is None:
929            keypairs = FakeKeypair.create_keypairs(count)
930        return mock.Mock(side_effect=keypairs)
931
932
933class FakeAvailabilityZone(object):
934    """Fake one or more compute availability zones (AZs)."""
935
936    @staticmethod
937    def create_one_availability_zone(attrs=None):
938        """Create a fake AZ.
939
940        :param Dictionary attrs:
941            A dictionary with all attributes
942        :return:
943            A FakeResource object with zoneName, zoneState, etc.
944        """
945        attrs = attrs or {}
946
947        # Set default attributes.
948        host_name = uuid.uuid4().hex
949        service_name = uuid.uuid4().hex
950        service_updated_at = uuid.uuid4().hex
951        availability_zone = {
952            'zoneName': uuid.uuid4().hex,
953            'zoneState': {'available': True},
954            'hosts': {host_name: {service_name: {
955                'available': True,
956                'active': True,
957                'updated_at': service_updated_at,
958            }}},
959        }
960
961        # Overwrite default attributes.
962        availability_zone.update(attrs)
963
964        availability_zone = fakes.FakeResource(
965            info=copy.deepcopy(availability_zone),
966            loaded=True)
967        return availability_zone
968
969    @staticmethod
970    def create_availability_zones(attrs=None, count=2):
971        """Create multiple fake AZs.
972
973        :param Dictionary attrs:
974            A dictionary with all attributes
975        :param int count:
976            The number of AZs to fake
977        :return:
978            A list of FakeResource objects faking the AZs
979        """
980        availability_zones = []
981        for i in range(0, count):
982            availability_zone = \
983                FakeAvailabilityZone.create_one_availability_zone(attrs)
984            availability_zones.append(availability_zone)
985
986        return availability_zones
987
988
989class FakeFloatingIP(object):
990    """Fake one or more floating ip."""
991
992    @staticmethod
993    def create_one_floating_ip(attrs=None):
994        """Create a fake floating ip.
995
996        :param Dictionary attrs:
997            A dictionary with all attributes
998        :return:
999            A FakeResource object, with id, ip, and so on
1000        """
1001        attrs = attrs or {}
1002
1003        # Set default attributes.
1004        floating_ip_attrs = {
1005            'id': 'floating-ip-id-' + uuid.uuid4().hex,
1006            'ip': '1.0.9.0',
1007            'fixed_ip': '2.0.9.0',
1008            'instance_id': 'server-id-' + uuid.uuid4().hex,
1009            'pool': 'public',
1010        }
1011
1012        # Overwrite default attributes.
1013        floating_ip_attrs.update(attrs)
1014
1015        return floating_ip_attrs
1016
1017    @staticmethod
1018    def create_floating_ips(attrs=None, count=2):
1019        """Create multiple fake floating ips.
1020
1021        :param Dictionary attrs:
1022            A dictionary with all attributes
1023        :param int count:
1024            The number of floating ips to fake
1025        :return:
1026            A list of FakeResource objects faking the floating ips
1027        """
1028        floating_ips = []
1029        for i in range(0, count):
1030            floating_ips.append(FakeFloatingIP.create_one_floating_ip(attrs))
1031        return floating_ips
1032
1033    @staticmethod
1034    def get_floating_ips(floating_ips=None, count=2):
1035        """Get an iterable MagicMock object with a list of faked floating ips.
1036
1037        If floating_ips list is provided, then initialize the Mock object
1038        with the list. Otherwise create one.
1039
1040        :param List floating_ips:
1041            A list of FakeResource objects faking floating ips
1042        :param int count:
1043            The number of floating ips to fake
1044        :return:
1045            An iterable Mock object with side_effect set to a list of faked
1046            floating ips
1047        """
1048        if floating_ips is None:
1049            floating_ips = FakeFloatingIP.create_floating_ips(count)
1050        return mock.Mock(side_effect=floating_ips)
1051
1052
1053class FakeFloatingIPPool(object):
1054    """Fake one or more floating ip pools."""
1055
1056    @staticmethod
1057    def create_one_floating_ip_pool(attrs=None):
1058        """Create a fake floating ip pool.
1059
1060        :param Dictionary attrs:
1061            A dictionary with all attributes
1062        :return:
1063            A FakeResource object, with name, etc
1064        """
1065        if attrs is None:
1066            attrs = {}
1067
1068        # Set default attributes.
1069        floating_ip_pool_attrs = {
1070            'name': 'floating-ip-pool-name-' + uuid.uuid4().hex,
1071        }
1072
1073        # Overwrite default attributes.
1074        floating_ip_pool_attrs.update(attrs)
1075
1076        return floating_ip_pool_attrs
1077
1078    @staticmethod
1079    def create_floating_ip_pools(attrs=None, count=2):
1080        """Create multiple fake floating ip pools.
1081
1082        :param Dictionary attrs:
1083            A dictionary with all attributes
1084        :param int count:
1085            The number of floating ip pools to fake
1086        :return:
1087            A list of FakeResource objects faking the floating ip pools
1088        """
1089        floating_ip_pools = []
1090        for i in range(0, count):
1091            floating_ip_pools.append(
1092                FakeFloatingIPPool.create_one_floating_ip_pool(attrs)
1093            )
1094        return floating_ip_pools
1095
1096
1097class FakeNetwork(object):
1098    """Fake one or more networks."""
1099
1100    @staticmethod
1101    def create_one_network(attrs=None):
1102        """Create a fake network.
1103
1104        :param Dictionary attrs:
1105            A dictionary with all attributes
1106        :return:
1107            A FakeResource object, with id, label, cidr and so on
1108        """
1109        attrs = attrs or {}
1110
1111        # Set default attributes.
1112        network_attrs = {
1113            'bridge': 'br100',
1114            'bridge_interface': None,
1115            'broadcast': '10.0.0.255',
1116            'cidr': '10.0.0.0/24',
1117            'cidr_v6': None,
1118            'created_at': '2016-02-11T11:17:37.000000',
1119            'deleted': False,
1120            'deleted_at': None,
1121            'dhcp_server': '10.0.0.1',
1122            'dhcp_start': '10.0.0.2',
1123            'dns1': '8.8.4.4',
1124            'dns2': None,
1125            'enable_dhcp': True,
1126            'gateway': '10.0.0.1',
1127            'gateway_v6': None,
1128            'host': None,
1129            'id': 'network-id-' + uuid.uuid4().hex,
1130            'injected': False,
1131            'label': 'network-label-' + uuid.uuid4().hex,
1132            'mtu': None,
1133            'multi_host': False,
1134            'netmask': '255.255.255.0',
1135            'netmask_v6': None,
1136            'priority': None,
1137            'project_id': 'project-id-' + uuid.uuid4().hex,
1138            'rxtx_base': None,
1139            'share_address': False,
1140            'updated_at': None,
1141            'vlan': None,
1142            'vpn_private_address': None,
1143            'vpn_public_address': None,
1144            'vpn_public_port': None,
1145        }
1146
1147        # Overwrite default attributes.
1148        network_attrs.update(attrs)
1149
1150        return network_attrs
1151
1152    @staticmethod
1153    def create_networks(attrs=None, count=2):
1154        """Create multiple fake networks.
1155
1156        :param Dictionary attrs:
1157            A dictionary with all attributes
1158        :param int count:
1159            The number of networks to fake
1160        :return:
1161            A list of FakeResource objects faking the networks
1162        """
1163        networks = []
1164        for i in range(0, count):
1165            networks.append(FakeNetwork.create_one_network(attrs))
1166
1167        return networks
1168
1169    @staticmethod
1170    def get_networks(networks=None, count=2):
1171        """Get an iterable MagicMock object with a list of faked networks.
1172
1173        If networks list is provided, then initialize the Mock object with the
1174        list. Otherwise create one.
1175
1176        :param List networks:
1177            A list of FakeResource objects faking networks
1178        :param int count:
1179            The number of networks to fake
1180        :return:
1181            An iterable Mock object with side_effect set to a list of faked
1182            networks
1183        """
1184        if networks is None:
1185            networks = FakeNetwork.create_networks(count=count)
1186        return mock.Mock(side_effect=networks)
1187
1188
1189class FakeHost(object):
1190    """Fake one host."""
1191
1192    @staticmethod
1193    def create_one_host(attrs=None):
1194        """Create a fake host.
1195
1196        :param Dictionary attrs:
1197            A dictionary with all attributes
1198        :return:
1199            A FakeResource object, with uuid and other attributes
1200        """
1201        attrs = attrs or {}
1202
1203        # Set default attributes.
1204        host_info = {
1205            "service_id": 1,
1206            "host": "host1",
1207            "uuid": 'host-id-' + uuid.uuid4().hex,
1208            "vcpus": 10,
1209            "memory_mb": 100,
1210            "local_gb": 100,
1211            "vcpus_used": 5,
1212            "memory_mb_used": 50,
1213            "local_gb_used": 10,
1214            "hypervisor_type": "xen",
1215            "hypervisor_version": 1,
1216            "hypervisor_hostname": "devstack1",
1217            "free_ram_mb": 50,
1218            "free_disk_gb": 50,
1219            "current_workload": 10,
1220            "running_vms": 1,
1221            "cpu_info": "",
1222            "disk_available_least": 1,
1223            "host_ip": "10.10.10.10",
1224            "supported_instances": "",
1225            "metrics": "",
1226            "pci_stats": "",
1227            "extra_resources": "",
1228            "stats": "",
1229            "numa_topology": "",
1230            "ram_allocation_ratio": 1.0,
1231            "cpu_allocation_ratio": 1.0,
1232            "zone": 'zone-' + uuid.uuid4().hex,
1233            "host_name": 'name-' + uuid.uuid4().hex,
1234            "service": 'service-' + uuid.uuid4().hex,
1235            "cpu": 4,
1236            "disk_gb": 100,
1237            'project': 'project-' + uuid.uuid4().hex,
1238        }
1239        host_info.update(attrs)
1240        return host_info
1241
1242
1243class FakeServerGroup(object):
1244    """Fake one server group"""
1245
1246    @staticmethod
1247    def create_one_server_group(attrs=None):
1248        """Create a fake server group
1249
1250        :param Dictionary attrs:
1251            A dictionary with all attributes
1252        :return:
1253            A FakeResource object, with id and other attributes
1254        """
1255        if attrs is None:
1256            attrs = {}
1257
1258        # Set default attributes.
1259        server_group_info = {
1260            'id': 'server-group-id-' + uuid.uuid4().hex,
1261            'members': [],
1262            'metadata': {},
1263            'name': 'server-group-name-' + uuid.uuid4().hex,
1264            'policies': [],
1265            'project_id': 'server-group-project-id-' + uuid.uuid4().hex,
1266            'user_id': 'server-group-user-id-' + uuid.uuid4().hex,
1267        }
1268
1269        # Overwrite default attributes.
1270        server_group_info.update(attrs)
1271
1272        server_group = fakes.FakeResource(
1273            info=copy.deepcopy(server_group_info),
1274            loaded=True)
1275        return server_group
1276
1277
1278class FakeUsage(object):
1279    """Fake one or more usage."""
1280
1281    @staticmethod
1282    def create_one_usage(attrs=None):
1283        """Create a fake usage.
1284
1285        :param Dictionary attrs:
1286            A dictionary with all attributes
1287        :return:
1288            A FakeResource object, with tenant_id and other attributes
1289        """
1290        if attrs is None:
1291            attrs = {}
1292
1293        # Set default attributes.
1294        usage_info = {
1295            'tenant_id': 'usage-tenant-id-' + uuid.uuid4().hex,
1296            'total_memory_mb_usage': 512.0,
1297            'total_vcpus_usage': 1.0,
1298            'total_local_gb_usage': 1.0,
1299            'server_usages': [
1300                {
1301                    'ended_at': None,
1302                    'flavor': 'usage-flavor-' + uuid.uuid4().hex,
1303                    'hours': 1.0,
1304                    'local_gb': 1,
1305                    'memory_mb': 512,
1306                    'name': 'usage-name-' + uuid.uuid4().hex,
1307                    'instance_id': uuid.uuid4().hex,
1308                    'state': 'active',
1309                    'uptime': 3600,
1310                    'vcpus': 1
1311                }
1312            ]
1313        }
1314
1315        # Overwrite default attributes.
1316        usage_info.update(attrs)
1317
1318        usage = fakes.FakeResource(info=copy.deepcopy(usage_info),
1319                                   loaded=True)
1320
1321        return usage
1322
1323    @staticmethod
1324    def create_usages(attrs=None, count=2):
1325        """Create multiple fake services.
1326
1327        :param Dictionary attrs:
1328            A dictionary with all attributes
1329        :param int count:
1330            The number of services to fake
1331        :return:
1332            A list of FakeResource objects faking the services
1333        """
1334        usages = []
1335        for i in range(0, count):
1336            usages.append(FakeUsage.create_one_usage(attrs))
1337
1338        return usages
1339
1340
1341class FakeQuota(object):
1342    """Fake quota"""
1343
1344    @staticmethod
1345    def create_one_comp_quota(attrs=None):
1346        """Create one quota"""
1347
1348        attrs = attrs or {}
1349
1350        quota_attrs = {
1351            'id': 'project-id-' + uuid.uuid4().hex,
1352            'cores': 20,
1353            'fixed_ips': 30,
1354            'injected_files': 100,
1355            'injected_file_content_bytes': 10240,
1356            'injected_file_path_bytes': 255,
1357            'instances': 50,
1358            'key_pairs': 20,
1359            'metadata_items': 10,
1360            'ram': 51200,
1361            'server_groups': 10,
1362            'server_group_members': 10
1363        }
1364
1365        quota_attrs.update(attrs)
1366        quota = fakes.FakeResource(
1367            info=copy.deepcopy(quota_attrs),
1368            loaded=True)
1369
1370        quota.project_id = quota_attrs['id']
1371
1372        return quota
1373
1374    @staticmethod
1375    def create_one_default_comp_quota(attrs=None):
1376        """Crate one quota"""
1377
1378        attrs = attrs or {}
1379
1380        quota_attrs = {
1381            'id': 'project-id-' + uuid.uuid4().hex,
1382            'cores': 10,
1383            'fixed_ips': 10,
1384            'injected_files': 100,
1385            'injected_file_content_bytes': 10240,
1386            'injected_file_path_bytes': 255,
1387            'instances': 20,
1388            'key_pairs': 20,
1389            'metadata_items': 10,
1390            'ram': 51200,
1391            'server_groups': 10,
1392            'server_group_members': 10
1393        }
1394
1395        quota_attrs.update(attrs)
1396        quota = fakes.FakeResource(
1397            info=copy.deepcopy(quota_attrs),
1398            loaded=True)
1399
1400        quota.project_id = quota_attrs['id']
1401
1402        return quota
1403
1404    @staticmethod
1405    def create_one_comp_detailed_quota(attrs=None):
1406        """Create one quota"""
1407
1408        attrs = attrs or {}
1409
1410        quota_attrs = {
1411            'id': 'project-id-' + uuid.uuid4().hex,
1412            'cores': {'reserved': 0, 'in_use': 0, 'limit': 20},
1413            'fixed_ips': {'reserved': 0, 'in_use': 0, 'limit': 30},
1414            'injected_files': {'reserved': 0, 'in_use': 0, 'limit': 100},
1415            'injected_file_content_bytes': {
1416                'reserved': 0, 'in_use': 0, 'limit': 10240},
1417            'injected_file_path_bytes': {
1418                'reserved': 0, 'in_use': 0, 'limit': 255},
1419            'instances': {'reserved': 0, 'in_use': 0, 'limit': 50},
1420            'key_pairs': {'reserved': 0, 'in_use': 0, 'limit': 20},
1421            'metadata_items': {'reserved': 0, 'in_use': 0, 'limit': 10},
1422            'ram': {'reserved': 0, 'in_use': 0, 'limit': 51200},
1423            'server_groups': {'reserved': 0, 'in_use': 0, 'limit': 10},
1424            'server_group_members': {'reserved': 0, 'in_use': 0, 'limit': 10}
1425        }
1426
1427        quota_attrs.update(attrs)
1428        quota = fakes.FakeResource(
1429            info=copy.deepcopy(quota_attrs),
1430            loaded=True)
1431
1432        quota.project_id = quota_attrs['id']
1433
1434        return quota
1435
1436
1437class FakeLimits(object):
1438    """Fake limits"""
1439
1440    def __init__(self, absolute_attrs=None, rate_attrs=None):
1441        self.absolute_limits_attrs = {
1442            'maxServerMeta': 128,
1443            'maxTotalInstances': 10,
1444            'maxPersonality': 5,
1445            'totalServerGroupsUsed': 0,
1446            'maxImageMeta': 128,
1447            'maxPersonalitySize': 10240,
1448            'maxTotalRAMSize': 51200,
1449            'maxServerGroups': 10,
1450            'maxSecurityGroupRules': 20,
1451            'maxTotalKeypairs': 100,
1452            'totalCoresUsed': 0,
1453            'totalRAMUsed': 0,
1454            'maxSecurityGroups': 10,
1455            'totalFloatingIpsUsed': 0,
1456            'totalInstancesUsed': 0,
1457            'maxServerGroupMembers': 10,
1458            'maxTotalFloatingIps': 10,
1459            'totalSecurityGroupsUsed': 0,
1460            'maxTotalCores': 20,
1461        }
1462        absolute_attrs = absolute_attrs or {}
1463        self.absolute_limits_attrs.update(absolute_attrs)
1464
1465        self.rate_limits_attrs = [{
1466            "uri": "*",
1467            "limit": [
1468                {
1469                    "value": 10,
1470                    "verb": "POST",
1471                    "remaining": 2,
1472                    "unit": "MINUTE",
1473                    "next-available": "2011-12-15T22:42:45Z"
1474                },
1475                {
1476                    "value": 10,
1477                    "verb": "PUT",
1478                    "remaining": 2,
1479                    "unit": "MINUTE",
1480                    "next-available": "2011-12-15T22:42:45Z"
1481                },
1482                {
1483                    "value": 100,
1484                    "verb": "DELETE",
1485                    "remaining": 100,
1486                    "unit": "MINUTE",
1487                    "next-available": "2011-12-15T22:42:45Z"
1488                }
1489            ]
1490        }]
1491
1492    @property
1493    def absolute(self):
1494        for (name, value) in self.absolute_limits_attrs.items():
1495            yield FakeAbsoluteLimit(name, value)
1496
1497    def absolute_limits(self):
1498        reference_data = []
1499        for (name, value) in self.absolute_limits_attrs.items():
1500            reference_data.append((name, value))
1501        return reference_data
1502
1503    @property
1504    def rate(self):
1505        for group in self.rate_limits_attrs:
1506            uri = group['uri']
1507            for rate in group['limit']:
1508                yield FakeRateLimit(rate['verb'], uri, rate['value'],
1509                                    rate['remaining'], rate['unit'],
1510                                    rate['next-available'])
1511
1512    def rate_limits(self):
1513        reference_data = []
1514        for group in self.rate_limits_attrs:
1515            uri = group['uri']
1516            for rate in group['limit']:
1517                reference_data.append((rate['verb'], uri, rate['value'],
1518                                      rate['remaining'], rate['unit'],
1519                                      rate['next-available']))
1520        return reference_data
1521
1522
1523class FakeAbsoluteLimit(object):
1524    """Data model that represents an absolute limit"""
1525
1526    def __init__(self, name, value):
1527        self.name = name
1528        self.value = value
1529
1530
1531class FakeRateLimit(object):
1532    """Data model that represents a flattened view of a single rate limit"""
1533
1534    def __init__(self, verb, uri, value, remain,
1535                 unit, next_available):
1536        self.verb = verb
1537        self.uri = uri
1538        self.value = value
1539        self.remain = remain
1540        self.unit = unit
1541        self.next_available = next_available
1542