Removing networking-l2gw as a dependency
[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 # if no network is returned and search by name create a new one
162 if not nets and filter_dict and filter_dict.get("name"):
163 net_id, net = self.new_network(filter_dict.get("name"), "mgmt")
164 nets.append(net)
165
166 return nets
167
168 def get_network(self, net_id):
169 if net_id not in self.nets:
170 raise vimconn.VimConnNotFoundException(
171 "network with id {} not found".format(net_id)
172 )
173
174 return self.nets[net_id]
175
176 def delete_network(self, net_id, created_items=None):
177 if net_id not in self.nets:
178 raise vimconn.VimConnNotFoundException(
179 "network with id {} not found".format(net_id)
180 )
181
182 self.logger.debug(
183 "delete network id={}, created_items={}".format(net_id, created_items)
184 )
185 self.nets.pop(net_id)
186
187 return net_id
188
189 def refresh_nets_status(self, net_list):
190 nets = {}
191
192 for net_id in net_list:
193 if net_id not in self.nets:
194 net = {"status": "DELETED"}
195 else:
196 net = self.nets[net_id].copy()
197 net["vim_info"] = yaml.dump(
198 {"status": "ACTIVE", "name": net["name"]},
199 default_flow_style=True,
200 width=256,
201 )
202
203 nets[net_id] = net
204
205 return nets
206
207 def get_flavor(self, flavor_id):
208 if flavor_id not in self.flavors:
209 raise vimconn.VimConnNotFoundException(
210 "flavor with id {} not found".format(flavor_id)
211 )
212
213 return self.flavors[flavor_id]
214
215 def new_flavor(self, flavor_data):
216 flavor_id = str(uuid4())
217 self.logger.debug(
218 "new flavor id={}, flavor_data={}".format(flavor_id, flavor_data)
219 )
220 flavor = deepcopy(flavor_data)
221 flavor["id"] = flavor_id
222
223 if "name" not in flavor:
224 flavor["name"] = flavor_id
225
226 self.flavors[flavor_id] = flavor
227
228 return flavor_id
229
230 def delete_flavor(self, flavor_id):
231 if flavor_id not in self.flavors:
232 raise vimconn.VimConnNotFoundException(
233 "flavor with id {} not found".format(flavor_id)
234 )
235
236 self.logger.debug("delete flavor id={}".format(flavor_id))
237 self.flavors.pop(flavor_id)
238
239 return flavor_id
240
241 def get_flavor_id_from_data(self, flavor_dict):
242 for flavor_id, flavor_data in self.flavors.items():
243 for k in ("ram", "vcpus", "disk", "extended"):
244 if flavor_data.get(k) != flavor_dict.get(k):
245 break
246 else:
247 return flavor_id
248
249 raise vimconn.VimConnNotFoundException(
250 "flavor with ram={} cpu={} disk={} {} not found".format(
251 flavor_dict["ram"],
252 flavor_dict["vcpus"],
253 flavor_dict["disk"],
254 "and extended" if flavor_dict.get("extended") else "",
255 )
256 )
257
258 def new_tenant(self, tenant_name, tenant_description):
259 tenant_id = str(uuid4())
260 self.logger.debug(
261 "new tenant id={}, description={}".format(tenant_id, tenant_description)
262 )
263 tenant = {
264 "name": tenant_name,
265 "description": tenant_description,
266 "id": tenant_id,
267 }
268 self.tenants[tenant_id] = tenant
269
270 return tenant_id
271
272 def delete_tenant(self, tenant_id):
273 if tenant_id not in self.tenants:
274 raise vimconn.VimConnNotFoundException(
275 "tenant with id {} not found".format(tenant_id)
276 )
277
278 self.tenants.pop(tenant_id)
279 self.logger.debug("delete tenant id={}".format(tenant_id))
280
281 return tenant_id
282
283 def get_tenant_list(self, filter_dict=None):
284 tenants = []
285
286 for tenant_id, tenant in self.tenants.items():
287 if filter_dict and filter_dict.get("name"):
288 if tenant["name"] != filter_dict.get("name"):
289 continue
290
291 if filter_dict and filter_dict.get("id"):
292 if tenant_id != filter_dict.get("id"):
293 continue
294
295 tenants.append(tenant)
296
297 return tenants
298
299 def new_image(self, image_dict):
300 image_id = str(uuid4())
301 self.logger.debug("new image id={}, iamge_dict={}".format(image_id, image_dict))
302 image = deepcopy(image_dict)
303 image["id"] = image_id
304
305 if "name" not in image:
306 image["id"] = image_id
307
308 self.images[image_id] = image
309
310 return image_id
311
312 def delete_image(self, image_id):
313 if image_id not in self.images:
314 raise vimconn.VimConnNotFoundException(
315 "image with id {} not found".format(image_id)
316 )
317
318 self.logger.debug("delete image id={}".format(image_id))
319 self.images.pop(image_id)
320
321 return image_id
322
323 def get_image_list(self, filter_dict=None):
324 images = []
325 for image_id, image in self.images.items():
326 if filter_dict and filter_dict.get("name"):
327 if image["name"] != filter_dict.get("name"):
328 continue
329
330 if filter_dict and filter_dict.get("checksum"):
331 if image["checksum"] != filter_dict.get("checksum"):
332 continue
333
334 if filter_dict and filter_dict.get("id"):
335 if image_id != filter_dict.get("id"):
336 continue
337
338 images.append(image)
339
340 return images
341
342 def new_vminstance(
343 self,
344 name,
345 description,
346 start,
347 image_id,
348 flavor_id,
349 net_list,
350 cloud_config=None,
351 disk_list=None,
352 availability_zone_index=None,
353 availability_zone_list=None,
354 ):
355 vm_id = str(uuid4())
356 interfaces = []
357 self.logger.debug(
358 "new vm id={}, name={}, image_id={}, flavor_id={}, net_list={}, cloud_config={}".format(
359 vm_id, name, image_id, flavor_id, net_list, cloud_config
360 )
361 )
362
363 for iface_index, iface in enumerate(net_list):
364 iface["vim_id"] = str(iface_index)
365 interface = {
366 "ip_address": iface.get("ip_address")
367 or self.config.get("vm_ip")
368 or "192.168.4.2",
369 "mac_address": iface.get("mac_address")
370 or self.config.get("vm_mac")
371 or "00:11:22:33:44:55",
372 "vim_interface_id": str(iface_index),
373 "vim_net_id": iface["net_id"],
374 }
375
376 if iface.get("type") in ("SR-IOV", "PCI-PASSTHROUGH") and self.config.get(
377 "sdn-port-mapping"
378 ):
379 compute_index = randrange(len(self.config["sdn-port-mapping"]))
380 port_index = randrange(
381 len(self.config["sdn-port-mapping"][compute_index]["ports"])
382 )
383 interface["compute_node"] = self.config["sdn-port-mapping"][
384 compute_index
385 ]["compute_node"]
386 interface["pci"] = self.config["sdn-port-mapping"][compute_index][
387 "ports"
388 ][port_index]["pci"]
389
390 interfaces.append(interface)
391
392 vm = {
393 "id": vm_id,
394 "name": name,
395 "status": "ACTIVE",
396 "description": description,
397 "interfaces": interfaces,
398 "image_id": image_id,
399 "flavor_id": flavor_id,
400 }
401
402 if image_id not in self.images:
403 self.logger.error(
404 "vm create, image_id '{}' not found. Skip".format(image_id)
405 )
406
407 if flavor_id not in self.flavors:
408 self.logger.error(
409 "vm create flavor_id '{}' not found. Skip".format(flavor_id)
410 )
411
412 self.vms[vm_id] = vm
413
414 return vm_id, vm
415
416 def get_vminstance(self, vm_id):
417 if vm_id not in self.vms:
418 raise vimconn.VimConnNotFoundException(
419 "vm with id {} not found".format(vm_id)
420 )
421
422 return self.vms[vm_id]
423
424 def delete_vminstance(self, vm_id, created_items=None):
425 if vm_id not in self.vms:
426 raise vimconn.VimConnNotFoundException(
427 "vm with id {} not found".format(vm_id)
428 )
429
430 self.vms.pop(vm_id)
431 self.logger.debug(
432 "delete vm id={}, created_items={}".format(vm_id, created_items)
433 )
434
435 return vm_id
436
437 def refresh_vms_status(self, vm_list):
438 vms = {}
439
440 for vm_id in vm_list:
441 if vm_id not in self.vms:
442 vm = {"status": "DELETED"}
443 else:
444 vm = deepcopy(self.vms[vm_id])
445 vm["vim_info"] = yaml.dump(
446 {"status": "ACTIVE", "name": vm["name"]},
447 default_flow_style=True,
448 width=256,
449 )
450
451 vms[vm_id] = vm
452
453 return vms
454
455 def action_vminstance(self, vm_id, action_dict, created_items={}):
456 return None
457
458 def inject_user_key(
459 self, ip_addr=None, user=None, key=None, ro_key=None, password=None
460 ):
461 if self.config.get("ssh_key"):
462 ro_key = self.config.get("ssh_key")
463
464 return super().inject_user_key(
465 ip_addr=ip_addr, user=user, key=key, ro_key=ro_key, password=password
466 )