Adding release_notes to tox
[osm/RO.git] / RO-plugin / osm_ro_plugin / vim_dummy.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2020 Telefonica Investigacion y Desarrollo, S.A.U.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 ##
18
19 """
20 Implements a Dummy vim plugin.
21 """
22
23 import yaml
24 from osm_ro_plugin import vimconn
25 from uuid import uuid4
26 from copy import deepcopy
27 import logging
28 from random import randrange
29
30 __author__ = "Alfonso Tierno"
31 __date__ = "2020-04-20"
32
33
34 class VimDummyConnector(vimconn.VimConnector):
35 """Dummy vim connector that does nothing
36 Provide config with:
37 vm_ip: ip address to provide at VM creation. For some tests must be a valid reachable VM
38 ssh_key: private ssh key to use for inserting an authorized ssh key
39 """
40
41 def __init__(
42 self,
43 uuid,
44 name,
45 tenant_id,
46 tenant_name,
47 url,
48 url_admin=None,
49 user=None,
50 passwd=None,
51 log_level=None,
52 config={},
53 persistent_info={},
54 ):
55 super().__init__(
56 uuid,
57 name,
58 tenant_id,
59 tenant_name,
60 url,
61 url_admin,
62 user,
63 passwd,
64 log_level,
65 config,
66 persistent_info,
67 )
68 self.logger = logging.getLogger("ro.vim.dummy")
69
70 if log_level:
71 self.logger.setLevel(getattr(logging, log_level))
72
73 self.nets = {
74 "mgmt": {
75 "id": "mgmt",
76 "name": "mgmt",
77 "status": "ACTIVE",
78 "vim_info": "{status: ACTIVE}",
79 }
80 }
81 self.vms = {}
82 self.flavors = {}
83 self.tenants = {}
84 # preload some images
85 self.images = {
86 "90681b39-dc09-49b7-ba2e-2c00c6b33b76": {
87 "id": "90681b39-dc09-49b7-ba2e-2c00c6b33b76",
88 "name": "cirros034",
89 "checksum": "ee1eca47dc88f4879d8a229cc70a07c6",
90 },
91 "83a39656-65db-47dc-af03-b55289115a53": {
92 "id": "",
93 "name": "cirros040",
94 "checksum": "443b7623e27ecf03dc9e01ee93f67afe",
95 },
96 "208314f2-8eb6-4101-965d-fe2ffbaedf3c": {
97 "id": "208314f2-8eb6-4101-965d-fe2ffbaedf3c",
98 "name": "ubuntu18.04",
99 "checksum": "b6fc7b9b91bca32e989e1edbcdeecb95",
100 },
101 "c03321f8-4b6e-4045-a309-1b3878bd32c1": {
102 "id": "c03321f8-4b6e-4045-a309-1b3878bd32c1",
103 "name": "ubuntu16.04",
104 "checksum": "8f08442faebad2d4a99fedb22fca11b5",
105 },
106 "4f6399a2-3554-457e-916e-ada01f8b950b": {
107 "id": "4f6399a2-3554-457e-916e-ada01f8b950b",
108 "name": "ubuntu1604",
109 "checksum": "8f08442faebad2d4a99fedb22fca11b5",
110 },
111 "59ac0b79-5c7d-4e83-b517-4c6c6a8ac1d3": {
112 "id": "59ac0b79-5c7d-4e83-b517-4c6c6a8ac1d3",
113 "name": "hackfest3-mgmt",
114 "checksum": "acec1e5d5ad7be9be7e6342a16bcf66a",
115 },
116 "f8818a03-f099-4c18-b1c7-26b1324203c1": {
117 "id": "f8818a03-f099-4c18-b1c7-26b1324203c1",
118 "name": "hackfest-pktgen",
119 "checksum": "f8818a03-f099-4c18-b1c7-26b1324203c1",
120 },
121 }
122
123 def new_network(
124 self,
125 net_name,
126 net_type,
127 ip_profile=None,
128 shared=False,
129 provider_network_profile=None,
130 ):
131 net_id = str(uuid4())
132 self.logger.debug(
133 "new network id={}, name={}, net_type={}, ip_profile={}, provider_network_profile={}".format(
134 net_id, net_name, net_type, ip_profile, provider_network_profile
135 )
136 )
137 net = {
138 "id": net_id,
139 "name": net_name,
140 "net_type": net_type,
141 "status": "ACTIVE",
142 }
143 self.nets[net_id] = net
144
145 return net_id, net
146
147 def get_network_list(self, filter_dict=None):
148 nets = []
149
150 for net_id, net in self.nets.items():
151 if filter_dict and filter_dict.get("name"):
152 if net["name"] != filter_dict.get("name"):
153 continue
154
155 if filter_dict and filter_dict.get("id"):
156 if net_id != filter_dict.get("id"):
157 continue
158
159 nets.append(net)
160
161 return nets
162
163 def get_network(self, net_id):
164 if net_id not in self.nets:
165 raise vimconn.VimConnNotFoundException(
166 "network with id {} not found".format(net_id)
167 )
168
169 return self.nets[net_id]
170
171 def delete_network(self, net_id, created_items=None):
172 if net_id not in self.nets:
173 raise vimconn.VimConnNotFoundException(
174 "network with id {} not found".format(net_id)
175 )
176
177 self.logger.debug(
178 "delete network id={}, created_items={}".format(net_id, created_items)
179 )
180 self.nets.pop(net_id)
181
182 return net_id
183
184 def refresh_nets_status(self, net_list):
185 nets = {}
186
187 for net_id in net_list:
188 if net_id not in self.nets:
189 net = {"status": "DELETED"}
190 else:
191 net = self.nets[net_id].copy()
192 net["vim_info"] = yaml.dump(
193 {"status": "ACTIVE", "name": net["name"]},
194 default_flow_style=True,
195 width=256,
196 )
197
198 nets[net_id] = net
199
200 return nets
201
202 def get_flavor(self, flavor_id):
203 if flavor_id not in self.flavors:
204 raise vimconn.VimConnNotFoundException(
205 "flavor with id {} not found".format(flavor_id)
206 )
207
208 return self.flavors[flavor_id]
209
210 def new_flavor(self, flavor_data):
211 flavor_id = str(uuid4())
212 self.logger.debug(
213 "new flavor id={}, flavor_data={}".format(flavor_id, flavor_data)
214 )
215 flavor = deepcopy(flavor_data)
216 flavor["id"] = flavor_id
217
218 if "name" not in flavor:
219 flavor["name"] = flavor_id
220
221 self.flavors[flavor_id] = flavor
222
223 return flavor_id
224
225 def delete_flavor(self, flavor_id):
226 if flavor_id not in self.flavors:
227 raise vimconn.VimConnNotFoundException(
228 "flavor with id {} not found".format(flavor_id)
229 )
230
231 self.logger.debug("delete flavor id={}".format(flavor_id))
232 self.flavors.pop(flavor_id)
233
234 return flavor_id
235
236 def get_flavor_id_from_data(self, flavor_dict):
237 for flavor_id, flavor_data in self.flavors.items():
238 for k in ("ram", "vcpus", "disk", "extended"):
239 if flavor_data.get(k) != flavor_dict.get(k):
240 break
241 else:
242 return flavor_id
243
244 raise vimconn.VimConnNotFoundException(
245 "flavor with ram={} cpu={} disk={} {} not found".format(
246 flavor_dict["ram"],
247 flavor_dict["vcpus"],
248 flavor_dict["disk"],
249 "and extended" if flavor_dict.get("extended") else "",
250 )
251 )
252
253 def new_tenant(self, tenant_name, tenant_description):
254 tenant_id = str(uuid4())
255 self.logger.debug(
256 "new tenant id={}, description={}".format(tenant_id, tenant_description)
257 )
258 tenant = {
259 "name": tenant_name,
260 "description": tenant_description,
261 "id": tenant_id,
262 }
263 self.tenants[tenant_id] = tenant
264
265 return tenant_id
266
267 def delete_tenant(self, tenant_id):
268 if tenant_id not in self.tenants:
269 raise vimconn.VimConnNotFoundException(
270 "tenant with id {} not found".format(tenant_id)
271 )
272
273 self.tenants.pop(tenant_id)
274 self.logger.debug("delete tenant id={}".format(tenant_id))
275
276 return tenant_id
277
278 def get_tenant_list(self, filter_dict=None):
279 tenants = []
280
281 for tenant_id, tenant in self.tenants.items():
282 if filter_dict and filter_dict.get("name"):
283 if tenant["name"] != filter_dict.get("name"):
284 continue
285
286 if filter_dict and filter_dict.get("id"):
287 if tenant_id != filter_dict.get("id"):
288 continue
289
290 tenants.append(tenant)
291
292 return tenants
293
294 def new_image(self, image_dict):
295 image_id = str(uuid4())
296 self.logger.debug("new image id={}, iamge_dict={}".format(image_id, image_dict))
297 image = deepcopy(image_dict)
298 image["id"] = image_id
299
300 if "name" not in image:
301 image["id"] = image_id
302
303 self.images[image_id] = image
304
305 return image_id
306
307 def delete_image(self, image_id):
308 if image_id not in self.images:
309 raise vimconn.VimConnNotFoundException(
310 "image with id {} not found".format(image_id)
311 )
312
313 self.logger.debug("delete image id={}".format(image_id))
314 self.images.pop(image_id)
315
316 return image_id
317
318 def get_image_list(self, filter_dict=None):
319 images = []
320 for image_id, image in self.images.items():
321 if filter_dict and filter_dict.get("name"):
322 if image["name"] != filter_dict.get("name"):
323 continue
324
325 if filter_dict and filter_dict.get("checksum"):
326 if image["checksum"] != filter_dict.get("checksum"):
327 continue
328
329 if filter_dict and filter_dict.get("id"):
330 if image_id != filter_dict.get("id"):
331 continue
332
333 images.append(image)
334
335 return images
336
337 def new_vminstance(
338 self,
339 name,
340 description,
341 start,
342 image_id,
343 flavor_id,
344 net_list,
345 cloud_config=None,
346 disk_list=None,
347 availability_zone_index=None,
348 availability_zone_list=None,
349 ):
350 vm_id = str(uuid4())
351 interfaces = []
352 self.logger.debug(
353 "new vm id={}, name={}, image_id={}, flavor_id={}, net_list={}, cloud_config={}".format(
354 vm_id, name, image_id, flavor_id, net_list, cloud_config
355 )
356 )
357
358 for iface_index, iface in enumerate(net_list):
359 iface["vim_id"] = str(iface_index)
360 interface = {
361 "ip_address": iface.get("ip_address")
362 or self.config.get("vm_ip")
363 or "192.168.4.2",
364 "mac_address": iface.get("mac_address")
365 or self.config.get("vm_mac")
366 or "00:11:22:33:44:55",
367 "vim_interface_id": str(iface_index),
368 "vim_net_id": iface["net_id"],
369 }
370
371 if iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH") and self.config.get(
372 "sdn-port-mapping"
373 ):
374 compute_index = randrange(len(self.config["sdn-port-mapping"]))
375 port_index = randrange(
376 len(self.config["sdn-port-mapping"][compute_index]["ports"])
377 )
378 interface["compute_node"] = self.config["sdn-port-mapping"][
379 compute_index
380 ]["compute_node"]
381 interface["pci"] = self.config["sdn-port-mapping"][compute_index][
382 "ports"
383 ][port_index]["pci"]
384
385 interfaces.append(interface)
386
387 vm = {
388 "id": vm_id,
389 "name": name,
390 "status": "ACTIVE",
391 "description": description,
392 "interfaces": interfaces,
393 "image_id": image_id,
394 "flavor_id": flavor_id,
395 }
396
397 if image_id not in self.images:
398 self.logger.error(
399 "vm create, image_id '{}' not found. Skip".format(image_id)
400 )
401
402 if flavor_id not in self.flavors:
403 self.logger.error(
404 "vm create flavor_id '{}' not found. Skip".format(flavor_id)
405 )
406
407 self.vms[vm_id] = vm
408
409 return vm_id, vm
410
411 def get_vminstance(self, vm_id):
412 if vm_id not in self.vms:
413 raise vimconn.VimConnNotFoundException(
414 "vm with id {} not found".format(vm_id)
415 )
416
417 return self.vms[vm_id]
418
419 def delete_vminstance(self, vm_id, created_items=None):
420 if vm_id not in self.vms:
421 raise vimconn.VimConnNotFoundException(
422 "vm with id {} not found".format(vm_id)
423 )
424
425 self.vms.pop(vm_id)
426 self.logger.debug(
427 "delete vm id={}, created_items={}".format(vm_id, created_items)
428 )
429
430 return vm_id
431
432 def refresh_vms_status(self, vm_list):
433 vms = {}
434
435 for vm_id in vm_list:
436 if vm_id not in self.vms:
437 vm = {"status": "DELETED"}
438 else:
439 vm = deepcopy(self.vms[vm_id])
440 vm["vim_info"] = yaml.dump(
441 {"status": "ACTIVE", "name": vm["name"]},
442 default_flow_style=True,
443 width=256,
444 )
445
446 vms[vm_id] = vm
447
448 return vms
449
450 def action_vminstance(self, vm_id, action_dict, created_items={}):
451 return None
452
453 def inject_user_key(
454 self, ip_addr=None, user=None, key=None, ro_key=None, password=None
455 ):
456 if self.config.get("ssh_key"):
457 ro_key = self.config.get("ssh_key")
458
459 return super().inject_user_key(
460 ip_addr=ip_addr, user=user, key=key, ro_key=ro_key, password=password
461 )