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