1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.V
12
13"""
14fakes
15----------------------------------
16
17Fakes used for testing
18"""
19
20import datetime
21import hashlib
22import json
23import uuid
24
25from openstack.cloud import meta
26from openstack.orchestration.util import template_format
27from openstack import utils
28
29PROJECT_ID = '1c36b64c840a42cd9e9b931a369337f0'
30FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8dddd'
31CHOCOLATE_FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8ddde'
32STRAWBERRY_FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8dddf'
33COMPUTE_ENDPOINT = 'https://compute.example.com/v2.1'
34ORCHESTRATION_ENDPOINT = 'https://orchestration.example.com/v1/{p}'.format(
35    p=PROJECT_ID)
36NO_MD5 = '93b885adfe0da089cdf634904fd59f71'
37NO_SHA256 = '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d'
38FAKE_PUBLIC_KEY = (
39    "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGj"
40    "lnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/"
41    "sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qg"
42    "fQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3P"
43    "HB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+"
44    "YIsBUHNLLMM/oQp Generated-by-Nova\n")
45
46
47def make_fake_flavor(flavor_id, name, ram=100, disk=1600, vcpus=24):
48    return {
49        u'OS-FLV-DISABLED:disabled': False,
50        u'OS-FLV-EXT-DATA:ephemeral': 0,
51        u'disk': disk,
52        u'id': flavor_id,
53        u'links': [{
54            u'href': u'{endpoint}/flavors/{id}'.format(
55                endpoint=COMPUTE_ENDPOINT, id=flavor_id),
56            u'rel': u'self'
57        }, {
58            u'href': u'{endpoint}/flavors/{id}'.format(
59                endpoint=COMPUTE_ENDPOINT, id=flavor_id),
60            u'rel': u'bookmark'
61        }],
62        u'name': name,
63        u'os-flavor-access:is_public': True,
64        u'ram': ram,
65        u'rxtx_factor': 1.0,
66        u'swap': u'',
67        u'vcpus': vcpus
68    }
69
70
71FAKE_FLAVOR = make_fake_flavor(FLAVOR_ID, 'vanilla')
72FAKE_CHOCOLATE_FLAVOR = make_fake_flavor(
73    CHOCOLATE_FLAVOR_ID, 'chocolate', ram=200)
74FAKE_STRAWBERRY_FLAVOR = make_fake_flavor(
75    STRAWBERRY_FLAVOR_ID, 'strawberry', ram=300)
76FAKE_FLAVOR_LIST = [FAKE_FLAVOR, FAKE_CHOCOLATE_FLAVOR, FAKE_STRAWBERRY_FLAVOR]
77FAKE_TEMPLATE = '''heat_template_version: 2014-10-16
78
79parameters:
80  length:
81    type: number
82    default: 10
83
84resources:
85  my_rand:
86    type: OS::Heat::RandomString
87    properties:
88      length: {get_param: length}
89outputs:
90  rand:
91    value:
92      get_attr: [my_rand, value]
93'''
94FAKE_TEMPLATE_CONTENT = template_format.parse(FAKE_TEMPLATE)
95
96
97def make_fake_server(
98        server_id, name, status='ACTIVE', admin_pass=None,
99        addresses=None, image=None, flavor=None):
100    if addresses is None:
101        if status == 'ACTIVE':
102            addresses = {
103                "private": [
104                    {
105                        "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
106                        "version": 6,
107                        "addr": "fddb:b018:307:0:f816:3eff:fedf:b08d",
108                        "OS-EXT-IPS:type": "fixed"},
109                    {
110                        "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
111                        "version": 4,
112                        "addr": "10.1.0.9",
113                        "OS-EXT-IPS:type": "fixed"},
114                    {
115                        "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
116                        "version": 4,
117                        "addr": "172.24.5.5",
118                        "OS-EXT-IPS:type": "floating"}]}
119        else:
120            addresses = {}
121    if image is None:
122        image = {"id": "217f3ab1-03e0-4450-bf27-63d52b421e9e",
123                 "links": []}
124    if flavor is None:
125        flavor = {"id": "64",
126                  "links": []}
127
128    server = {
129        "OS-EXT-STS:task_state": None,
130        "addresses": addresses,
131        "links": [],
132        "image": image,
133        "OS-EXT-STS:vm_state": "active",
134        "OS-SRV-USG:launched_at": "2017-03-23T23:57:38.000000",
135        "flavor": flavor,
136        "id": server_id,
137        "security_groups": [{"name": "default"}],
138        "user_id": "9c119f4beaaa438792ce89387362b3ad",
139        "OS-DCF:diskConfig": "MANUAL",
140        "accessIPv4": "",
141        "accessIPv6": "",
142        "progress": 0,
143        "OS-EXT-STS:power_state": 1,
144        "OS-EXT-AZ:availability_zone": "nova",
145        "metadata": {},
146        "status": status,
147        "updated": "2017-03-23T23:57:39Z",
148        "hostId": "89d165f04384e3ffa4b6536669eb49104d30d6ca832bba2684605dbc",
149        "OS-SRV-USG:terminated_at": None,
150        "key_name": None,
151        "name": name,
152        "created": "2017-03-23T23:57:12Z",
153        "tenant_id": PROJECT_ID,
154        "os-extended-volumes:volumes_attached": [],
155        "config_drive": "True"}
156    if admin_pass:
157        server['adminPass'] = admin_pass
158    return json.loads(json.dumps(server))
159
160
161def make_fake_keypair(name):
162    # Note: this is literally taken from:
163    # https://docs.openstack.org/api-ref/compute/
164    return {
165        "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd",
166        "name": name,
167        "type": "ssh",
168        "public_key": FAKE_PUBLIC_KEY,
169        "created_at": datetime.datetime.now().isoformat(),
170    }
171
172
173def make_fake_stack(id, name, description=None, status='CREATE_COMPLETE'):
174    return {
175        'creation_time': '2017-03-23T23:57:12Z',
176        'deletion_time': '2017-03-23T23:57:12Z',
177        'description': description,
178        'id': id,
179        'links': [],
180        'parent': None,
181        'stack_name': name,
182        'stack_owner': None,
183        'stack_status': status,
184        'stack_user_project_id': PROJECT_ID,
185        'tags': None,
186        'updated_time': '2017-03-23T23:57:12Z',
187    }
188
189
190def make_fake_stack_event(
191        id, name, status='CREATE_COMPLETED', resource_name='id'):
192    event_id = uuid.uuid4().hex
193    self_url = "{endpoint}/stacks/{name}/{id}/resources/{name}/events/{event}"
194    resource_url = "{endpoint}/stacks/{name}/{id}/resources/{name}"
195    return {
196        "resource_name": id if resource_name == 'id' else name,
197        "event_time": "2017-03-26T19:38:18",
198        "links": [
199            {
200                "href": self_url.format(
201                    endpoint=ORCHESTRATION_ENDPOINT,
202                    name=name, id=id, event=event_id),
203                "rel": "self"
204            }, {
205                "href": resource_url.format(
206                    endpoint=ORCHESTRATION_ENDPOINT,
207                    name=name, id=id),
208                "rel": "resource"
209            }, {
210                "href": "{endpoint}/stacks/{name}/{id}".format(
211                    endpoint=ORCHESTRATION_ENDPOINT,
212                    name=name, id=id),
213                "rel": "stack"
214            }],
215        "logical_resource_id": name,
216        "resource_status": status,
217        "resource_status_reason": "",
218        "physical_resource_id": id,
219        "id": event_id,
220    }
221
222
223def make_fake_image(
224        image_id=None, md5=NO_MD5, sha256=NO_SHA256, status='active',
225        image_name=u'fake_image',
226        data=None,
227        checksum=u'ee36e35a297980dee1b514de9803ec6d'):
228    if data:
229        md5 = utils.md5(usedforsecurity=False)
230        sha256 = hashlib.sha256()
231        with open(data, 'rb') as file_obj:
232            for chunk in iter(lambda: file_obj.read(8192), b''):
233                md5.update(chunk)
234                sha256.update(chunk)
235        md5 = md5.hexdigest()
236        sha256 = sha256.hexdigest()
237    return {
238        u'image_state': u'available',
239        u'container_format': u'bare',
240        u'min_ram': 0,
241        u'ramdisk_id': 'fake_ramdisk_id',
242        u'updated_at': u'2016-02-10T05:05:02Z',
243        u'file': '/v2/images/' + image_id + '/file',
244        u'size': 3402170368,
245        u'image_type': u'snapshot',
246        u'disk_format': u'qcow2',
247        u'id': image_id,
248        u'schema': u'/v2/schemas/image',
249        u'status': status,
250        u'tags': [],
251        u'visibility': u'private',
252        u'locations': [{
253            u'url': u'http://127.0.0.1/images/' + image_id,
254            u'metadata': {}}],
255        u'min_disk': 40,
256        u'virtual_size': None,
257        u'name': image_name,
258        u'checksum': md5 or checksum,
259        u'created_at': u'2016-02-10T05:03:11Z',
260        u'owner_specified.openstack.md5': md5 or NO_MD5,
261        u'owner_specified.openstack.sha256': sha256 or NO_SHA256,
262        u'owner_specified.openstack.object': 'images/{name}'.format(
263            name=image_name),
264        u'protected': False}
265
266
267def make_fake_machine(machine_name, machine_id=None):
268    if not machine_id:
269        machine_id = uuid.uuid4().hex
270    return meta.obj_to_munch(FakeMachine(
271        id=machine_id,
272        name=machine_name))
273
274
275def make_fake_port(address, node_id=None, port_id=None):
276    if not node_id:
277        node_id = uuid.uuid4().hex
278    if not port_id:
279        port_id = uuid.uuid4().hex
280    return meta.obj_to_munch(FakeMachinePort(
281        id=port_id,
282        address=address,
283        node_id=node_id))
284
285
286class FakeFloatingIP:
287    def __init__(self, id, pool, ip, fixed_ip, instance_id):
288        self.id = id
289        self.pool = pool
290        self.ip = ip
291        self.fixed_ip = fixed_ip
292        self.instance_id = instance_id
293
294
295def make_fake_server_group(id, name, policies):
296    return json.loads(json.dumps({
297        'id': id,
298        'name': name,
299        'policies': policies,
300        'members': [],
301        'metadata': {},
302    }))
303
304
305def make_fake_hypervisor(id, name):
306    return json.loads(json.dumps({
307        'id': id,
308        'hypervisor_hostname': name,
309        'state': 'up',
310        'status': 'enabled',
311        "cpu_info": {
312            "arch": "x86_64",
313            "model": "Nehalem",
314            "vendor": "Intel",
315            "features": [
316                "pge",
317                "clflush"
318            ],
319            "topology": {
320                "cores": 1,
321                "threads": 1,
322                "sockets": 4
323            }
324        },
325        "current_workload": 0,
326        "status": "enabled",
327        "state": "up",
328        "disk_available_least": 0,
329        "host_ip": "1.1.1.1",
330        "free_disk_gb": 1028,
331        "free_ram_mb": 7680,
332        "hypervisor_type": "fake",
333        "hypervisor_version": 1000,
334        "local_gb": 1028,
335        "local_gb_used": 0,
336        "memory_mb": 8192,
337        "memory_mb_used": 512,
338        "running_vms": 0,
339        "service": {
340            "host": "host1",
341            "id": 7,
342            "disabled_reason": None
343        },
344        "vcpus": 1,
345        "vcpus_used": 0
346    }))
347
348
349class FakeVolume:
350    def __init__(
351            self, id, status, name, attachments=[],
352            size=75):
353        self.id = id
354        self.status = status
355        self.name = name
356        self.attachments = attachments
357        self.size = size
358        self.snapshot_id = 'id:snapshot'
359        self.description = 'description'
360        self.volume_type = 'type:volume'
361        self.availability_zone = 'az1'
362        self.created_at = '1900-01-01 12:34:56'
363        self.source_volid = '12345'
364        self.metadata = {}
365
366
367class FakeVolumeSnapshot:
368    def __init__(
369            self, id, status, name, description, size=75):
370        self.id = id
371        self.status = status
372        self.name = name
373        self.description = description
374        self.size = size
375        self.created_at = '1900-01-01 12:34:56'
376        self.volume_id = '12345'
377        self.metadata = {}
378
379
380class FakeMachine:
381    def __init__(self, id, name=None, driver=None, driver_info=None,
382                 chassis_uuid=None, instance_info=None, instance_uuid=None,
383                 properties=None, reservation=None, last_error=None,
384                 provision_state='available'):
385        self.uuid = id
386        self.name = name
387        self.driver = driver
388        self.driver_info = driver_info
389        self.chassis_uuid = chassis_uuid
390        self.instance_info = instance_info
391        self.instance_uuid = instance_uuid
392        self.properties = properties
393        self.reservation = reservation
394        self.last_error = last_error
395        self.provision_state = provision_state
396
397
398class FakeMachinePort:
399    def __init__(self, id, address, node_id):
400        self.uuid = id
401        self.address = address
402        self.node_uuid = node_id
403
404
405def make_fake_neutron_security_group(
406        id, name, description, rules, stateful=True, project_id=None):
407    if not rules:
408        rules = []
409    if not project_id:
410        project_id = PROJECT_ID
411    return json.loads(json.dumps({
412        'id': id,
413        'name': name,
414        'description': description,
415        'stateful': stateful,
416        'project_id': project_id,
417        'tenant_id': project_id,
418        'security_group_rules': rules,
419    }))
420
421
422def make_fake_nova_security_group_rule(
423        id, from_port, to_port, ip_protocol, cidr):
424    return json.loads(json.dumps({
425        'id': id,
426        'from_port': int(from_port),
427        'to_port': int(to_port),
428        'ip_protcol': 'tcp',
429        'ip_range': {
430            'cidr': cidr
431        }
432    }))
433
434
435def make_fake_nova_security_group(id, name, description, rules):
436    if not rules:
437        rules = []
438    return json.loads(json.dumps({
439        'id': id,
440        'name': name,
441        'description': description,
442        'tenant_id': PROJECT_ID,
443        'rules': rules,
444    }))
445
446
447class FakeNovaSecgroupRule:
448    def __init__(self, id, from_port=None, to_port=None, ip_protocol=None,
449                 cidr=None, parent_group_id=None):
450        self.id = id
451        self.from_port = from_port
452        self.to_port = to_port
453        self.ip_protocol = ip_protocol
454        if cidr:
455            self.ip_range = {'cidr': cidr}
456        self.parent_group_id = parent_group_id
457
458
459class FakeHypervisor:
460    def __init__(self, id, hostname):
461        self.id = id
462        self.hypervisor_hostname = hostname
463
464
465class FakeZone:
466    def __init__(self, id, name, type_, email, description,
467                 ttl, masters):
468        self.id = id
469        self.name = name
470        self.type_ = type_
471        self.email = email
472        self.description = description
473        self.ttl = ttl
474        self.masters = masters
475
476
477class FakeRecordset:
478    def __init__(self, zone, id, name, type_, description,
479                 ttl, records):
480        self.zone = zone
481        self.id = id
482        self.name = name
483        self.type_ = type_
484        self.description = description
485        self.ttl = ttl
486        self.records = records
487
488
489def make_fake_aggregate(id, name, availability_zone='nova',
490                        metadata=None, hosts=None):
491    if not metadata:
492        metadata = {}
493    if not hosts:
494        hosts = []
495    return json.loads(json.dumps({
496        "availability_zone": availability_zone,
497        "created_at": datetime.datetime.now().isoformat(),
498        "deleted": False,
499        "deleted_at": None,
500        "hosts": hosts,
501        "id": int(id),
502        "metadata": {
503            "availability_zone": availability_zone,
504        },
505        "name": name,
506        "updated_at": None,
507    }))
508