blob: e88cecc2f9fc8dd227bdf67fcd6b23d1e6f196b4 [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
tierno92021022018-09-12 16:29:23 +02004# Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
tierno7edb6752016-03-21 17:37:52 +01005# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
sousaedu80135b92021-02-17 15:05:18 +010024"""
tierno7edb6752016-03-21 17:37:52 +010025vimconnector implements all the methods to interact with openvim using the openvim API.
sousaedu80135b92021-02-17 15:05:18 +010026"""
27__author__ = "Alfonso Tierno, Gerardo Garcia"
28__date__ = "$26-aug-2014 11:09:29$"
tierno7edb6752016-03-21 17:37:52 +010029
tierno7edb6752016-03-21 17:37:52 +010030import json
tiernoae4a8d12016-07-08 12:30:39 +020031import logging
tierno66eba6e2017-11-10 17:09:18 +010032import math
sousaedu049cbb12022-01-05 11:39:35 +000033from urllib.parse import quote
34
35from jsonschema import exceptions as js_e, validate as js_v
sousaedu80135b92021-02-17 15:05:18 +010036from osm_ro.openmano_schemas import (
sousaedu049cbb12022-01-05 11:39:35 +000037 description_schema,
sousaedu80135b92021-02-17 15:05:18 +010038 id_schema,
sousaedu049cbb12022-01-05 11:39:35 +000039 integer0_schema,
sousaedu80135b92021-02-17 15:05:18 +010040 name_schema,
41 nameshort_schema,
sousaedu80135b92021-02-17 15:05:18 +010042 vlan1000_schema,
sousaedu80135b92021-02-17 15:05:18 +010043)
sousaedu049cbb12022-01-05 11:39:35 +000044from osm_ro_plugin import vimconn
45import requests
46import yaml
tierno7edb6752016-03-21 17:37:52 +010047
sousaedu80135b92021-02-17 15:05:18 +010048"""contain the openvim virtual machine status to openmano status"""
49vmStatus2manoFormat = {
50 "ACTIVE": "ACTIVE",
51 "PAUSED": "PAUSED",
52 "SUSPENDED": "SUSPENDED",
53 "INACTIVE": "INACTIVE",
54 "CREATING": "BUILD",
55 "ERROR": "ERROR",
56 "DELETED": "DELETED",
57}
58netStatus2manoFormat = {
59 "ACTIVE": "ACTIVE",
60 "INACTIVE": "INACTIVE",
61 "BUILD": "BUILD",
62 "ERROR": "ERROR",
63 "DELETED": "DELETED",
64 "DOWN": "DOWN",
65}
tierno7edb6752016-03-21 17:37:52 +010066
67
68host_schema = {
sousaedu80135b92021-02-17 15:05:18 +010069 "type": "object",
70 "properties": {
tierno7edb6752016-03-21 17:37:52 +010071 "id": id_schema,
72 "name": name_schema,
73 },
sousaedu80135b92021-02-17 15:05:18 +010074 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +010075}
76image_schema = {
sousaedu80135b92021-02-17 15:05:18 +010077 "type": "object",
78 "properties": {
tierno7edb6752016-03-21 17:37:52 +010079 "id": id_schema,
80 "name": name_schema,
81 },
sousaedu80135b92021-02-17 15:05:18 +010082 "required": ["id", "name"],
tierno7edb6752016-03-21 17:37:52 +010083}
84server_schema = {
sousaedu80135b92021-02-17 15:05:18 +010085 "type": "object",
86 "properties": {
87 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +010088 "name": name_schema,
89 },
sousaedu80135b92021-02-17 15:05:18 +010090 "required": ["id", "name"],
tierno7edb6752016-03-21 17:37:52 +010091}
92new_host_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +010093 "title": "host response information schema",
tierno7edb6752016-03-21 17:37:52 +010094 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +010095 "type": "object",
96 "properties": {"host": host_schema},
tierno7edb6752016-03-21 17:37:52 +010097 "required": ["host"],
sousaedu80135b92021-02-17 15:05:18 +010098 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +010099}
100
101get_images_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100102 "title": "openvim images response information schema",
tierno7edb6752016-03-21 17:37:52 +0100103 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100104 "type": "object",
105 "properties": {
106 "images": {
107 "type": "array",
tierno7edb6752016-03-21 17:37:52 +0100108 "items": image_schema,
109 }
110 },
111 "required": ["images"],
sousaedu80135b92021-02-17 15:05:18 +0100112 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100113}
114
115get_hosts_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100116 "title": "openvim hosts response information schema",
tierno7edb6752016-03-21 17:37:52 +0100117 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100118 "type": "object",
119 "properties": {
120 "hosts": {
121 "type": "array",
tierno7edb6752016-03-21 17:37:52 +0100122 "items": host_schema,
123 }
124 },
125 "required": ["hosts"],
sousaedu80135b92021-02-17 15:05:18 +0100126 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100127}
128
sousaedu80135b92021-02-17 15:05:18 +0100129get_host_detail_response_schema = (
130 new_host_response_schema # TODO: Content is not parsed yet
131)
tierno7edb6752016-03-21 17:37:52 +0100132
133get_server_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100134 "title": "openvim server response information schema",
tierno7edb6752016-03-21 17:37:52 +0100135 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100136 "type": "object",
137 "properties": {
138 "servers": {
139 "type": "array",
tierno7edb6752016-03-21 17:37:52 +0100140 "items": server_schema,
141 }
142 },
143 "required": ["servers"],
sousaedu80135b92021-02-17 15:05:18 +0100144 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100145}
146
147new_tenant_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100148 "title": "tenant response information schema",
tierno7edb6752016-03-21 17:37:52 +0100149 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100150 "type": "object",
151 "properties": {
152 "tenant": {
153 "type": "object",
154 "properties": {
tierno7edb6752016-03-21 17:37:52 +0100155 "id": id_schema,
156 "name": nameshort_schema,
sousaedu80135b92021-02-17 15:05:18 +0100157 "description": description_schema,
158 "enabled": {"type": "boolean"},
tierno7edb6752016-03-21 17:37:52 +0100159 },
sousaedu80135b92021-02-17 15:05:18 +0100160 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100161 }
162 },
163 "required": ["tenant"],
sousaedu80135b92021-02-17 15:05:18 +0100164 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100165}
166
167new_network_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100168 "title": "network response information schema",
tierno7edb6752016-03-21 17:37:52 +0100169 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100170 "type": "object",
171 "properties": {
172 "network": {
173 "type": "object",
174 "properties": {
175 "id": id_schema,
176 "name": name_schema,
177 "type": {
178 "type": "string",
179 "enum": ["bridge_man", "bridge_data", "data", "ptp"],
180 },
181 "shared": {"type": "boolean"},
182 "tenant_id": id_schema,
183 "admin_state_up": {"type": "boolean"},
184 "vlan": vlan1000_schema,
tierno7edb6752016-03-21 17:37:52 +0100185 },
sousaedu80135b92021-02-17 15:05:18 +0100186 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100187 }
188 },
189 "required": ["network"],
sousaedu80135b92021-02-17 15:05:18 +0100190 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100191}
192
193
194# get_network_response_schema = {
195# "title":"get network response information schema",
196# "$schema": "http://json-schema.org/draft-04/schema#",
197# "type":"object",
198# "properties":{
199# "network":{
200# "type":"object",
201# "properties":{
202# "id":id_schema,
203# "name":name_schema,
204# "type":{"type":"string", "enum":["bridge_man","bridge_data","data", "ptp"]},
205# "shared":{"type":"boolean"},
206# "tenant_id":id_schema,
207# "admin_state_up":{"type":"boolean"},
208# "vlan":vlan1000_schema
209# },
210# "required": ["id"]
211# }
212# },
213# "required": ["network"],
214# "additionalProperties": False
215# }
216
217
218new_port_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100219 "title": "port response information schema",
tierno7edb6752016-03-21 17:37:52 +0100220 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100221 "type": "object",
222 "properties": {
223 "port": {
224 "type": "object",
225 "properties": {
226 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +0100227 },
sousaedu80135b92021-02-17 15:05:18 +0100228 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100229 }
230 },
231 "required": ["port"],
sousaedu80135b92021-02-17 15:05:18 +0100232 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100233}
234
235get_flavor_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100236 "title": "openvim flavors response information schema",
tierno7edb6752016-03-21 17:37:52 +0100237 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100238 "type": "object",
239 "properties": {
240 "flavor": {
241 "type": "object",
242 "properties": {
243 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +0100244 "name": name_schema,
sousaedu80135b92021-02-17 15:05:18 +0100245 "extended": {"type": "object"},
tierno7edb6752016-03-21 17:37:52 +0100246 },
247 "required": ["id", "name"],
248 }
249 },
250 "required": ["flavor"],
sousaedu80135b92021-02-17 15:05:18 +0100251 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100252}
253
254new_flavor_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100255 "title": "flavor response information schema",
tierno7edb6752016-03-21 17:37:52 +0100256 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100257 "type": "object",
258 "properties": {
259 "flavor": {
260 "type": "object",
261 "properties": {
262 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +0100263 },
sousaedu80135b92021-02-17 15:05:18 +0100264 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100265 }
266 },
267 "required": ["flavor"],
sousaedu80135b92021-02-17 15:05:18 +0100268 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100269}
270
tiernoae4a8d12016-07-08 12:30:39 +0200271get_image_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100272 "title": "openvim images response information schema",
tiernoae4a8d12016-07-08 12:30:39 +0200273 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100274 "type": "object",
275 "properties": {
276 "image": {
277 "type": "object",
278 "properties": {
279 "id": id_schema,
tiernoae4a8d12016-07-08 12:30:39 +0200280 "name": name_schema,
281 },
282 "required": ["id", "name"],
283 }
284 },
285 "required": ["flavor"],
sousaedu80135b92021-02-17 15:05:18 +0100286 "additionalProperties": False,
tiernoae4a8d12016-07-08 12:30:39 +0200287}
tierno7edb6752016-03-21 17:37:52 +0100288new_image_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100289 "title": "image response information schema",
tierno7edb6752016-03-21 17:37:52 +0100290 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100291 "type": "object",
292 "properties": {
293 "image": {
294 "type": "object",
295 "properties": {
296 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +0100297 },
sousaedu80135b92021-02-17 15:05:18 +0100298 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100299 }
300 },
301 "required": ["image"],
sousaedu80135b92021-02-17 15:05:18 +0100302 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100303}
304
305new_vminstance_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100306 "title": "server response information schema",
tierno7edb6752016-03-21 17:37:52 +0100307 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100308 "type": "object",
309 "properties": {
310 "server": {
311 "type": "object",
312 "properties": {
313 "id": id_schema,
tierno7edb6752016-03-21 17:37:52 +0100314 },
sousaedu80135b92021-02-17 15:05:18 +0100315 "required": ["id"],
tierno7edb6752016-03-21 17:37:52 +0100316 }
317 },
318 "required": ["server"],
sousaedu80135b92021-02-17 15:05:18 +0100319 "additionalProperties": False,
tierno7edb6752016-03-21 17:37:52 +0100320}
321
322get_processor_rankings_response_schema = {
sousaedu80135b92021-02-17 15:05:18 +0100323 "title": "processor rankings information schema",
tierno7edb6752016-03-21 17:37:52 +0100324 "$schema": "http://json-schema.org/draft-04/schema#",
sousaedu80135b92021-02-17 15:05:18 +0100325 "type": "object",
326 "properties": {
327 "rankings": {
328 "type": "array",
329 "items": {
330 "type": "object",
331 "properties": {"model": description_schema, "value": integer0_schema},
tierno7edb6752016-03-21 17:37:52 +0100332 "additionalProperties": False,
sousaedu80135b92021-02-17 15:05:18 +0100333 "required": ["model", "value"],
334 },
tierno7edb6752016-03-21 17:37:52 +0100335 },
336 "additionalProperties": False,
sousaedu80135b92021-02-17 15:05:18 +0100337 "required": ["rankings"],
338 },
tierno7edb6752016-03-21 17:37:52 +0100339}
340
tierno031ded62019-11-14 14:06:31 +0000341
tierno72774862020-05-04 11:44:15 +0000342class vimconnector(vimconn.VimConnector):
sousaedu80135b92021-02-17 15:05:18 +0100343 def __init__(
344 self,
345 uuid,
346 name,
347 tenant_id,
348 tenant_name,
349 url,
350 url_admin=None,
351 user=None,
352 passwd=None,
353 log_level="DEBUG",
354 config={},
355 persistent_info={},
356 ):
357 vimconn.VimConnector.__init__(
358 self,
359 uuid,
360 name,
361 tenant_id,
362 tenant_name,
363 url,
364 url_admin,
365 user,
366 passwd,
367 log_level,
368 config,
369 )
tierno392f2852016-05-13 12:28:55 +0200370 self.tenant = None
sousaedu80135b92021-02-17 15:05:18 +0100371 self.headers_req = {"content-type": "application/json"}
372 self.logger = logging.getLogger("ro.vim.openvim")
tiernob3d36742017-03-03 23:51:05 +0100373 self.persistent_info = persistent_info
tierno392f2852016-05-13 12:28:55 +0200374 if tenant_id:
375 self.tenant = tenant_id
376
sousaedu80135b92021-02-17 15:05:18 +0100377 def __setitem__(self, index, value):
378 """Set individuals parameters
tierno392f2852016-05-13 12:28:55 +0200379 Throw TypeError, KeyError
sousaedu80135b92021-02-17 15:05:18 +0100380 """
381 if index == "tenant_id":
tierno392f2852016-05-13 12:28:55 +0200382 self.tenant = value
sousaedu80135b92021-02-17 15:05:18 +0100383 elif index == "tenant_name":
tierno392f2852016-05-13 12:28:55 +0200384 self.tenant = None
sousaedu80135b92021-02-17 15:05:18 +0100385 vimconn.VimConnector.__setitem__(self, index, value)
tierno392f2852016-05-13 12:28:55 +0200386
387 def _get_my_tenant(self):
sousaedu80135b92021-02-17 15:05:18 +0100388 """Obtain uuid of my tenant from name"""
tierno392f2852016-05-13 12:28:55 +0200389 if self.tenant:
390 return self.tenant
391
sousaedu80135b92021-02-17 15:05:18 +0100392 url = self.url + "/tenants?name=" + quote(self.tenant_name)
tiernoae4a8d12016-07-08 12:30:39 +0200393 self.logger.info("Getting VIM tenant_id GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100394 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200395 self._check_http_request_response(vim_response)
396 try:
397 tenant_list = vim_response.json()["tenants"]
398 if len(tenant_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +0100399 raise vimconn.VimConnNotFoundException(
400 "No tenant found for name '{}'".format(self.tenant_name)
401 )
tiernoae4a8d12016-07-08 12:30:39 +0200402 elif len(tenant_list) > 1:
sousaedu80135b92021-02-17 15:05:18 +0100403 raise vimconn.VimConnConflictException(
404 "More that one tenant found for name '{}'".format(self.tenant_name)
405 )
tiernoae4a8d12016-07-08 12:30:39 +0200406 self.tenant = tenant_list[0]["id"]
407 return self.tenant
408 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +0100409 raise vimconn.VimConnUnexpectedResponse(
410 "Get VIM tenant {} '{}'".format(type(e).__name__, str(e))
411 )
tierno392f2852016-05-13 12:28:55 +0200412
sousaedu80135b92021-02-17 15:05:18 +0100413 def _format_jsonerror(self, http_response):
414 # DEPRECATED, to delete in the future
tierno7edb6752016-03-21 17:37:52 +0100415 try:
416 data = http_response.json()
417 return data["error"]["description"]
sousaedu80135b92021-02-17 15:05:18 +0100418 except Exception:
tierno7edb6752016-03-21 17:37:52 +0100419 return http_response.text
420
421 def _format_in(self, http_response, schema):
sousaedu80135b92021-02-17 15:05:18 +0100422 # DEPRECATED, to delete in the future
tierno7edb6752016-03-21 17:37:52 +0100423 try:
424 client_data = http_response.json()
425 js_v(client_data, schema)
sousaedu80135b92021-02-17 15:05:18 +0100426 # print "Input data: ", str(client_data)
tierno7edb6752016-03-21 17:37:52 +0100427 return True, client_data
venkatamahesh6ecca182017-01-27 23:04:40 +0530428 except js_e.ValidationError as exc:
sousaedu80135b92021-02-17 15:05:18 +0100429 print(
430 "validate_in error, jsonschema exception ", exc.message, "at", exc.path
431 )
432 return False, (
433 "validate_in error, jsonschema exception ",
434 exc.message,
435 "at",
436 exc.path,
437 )
438
tierno7edb6752016-03-21 17:37:52 +0100439 def _remove_extra_items(self, data, schema):
sousaedu80135b92021-02-17 15:05:18 +0100440 deleted = []
tierno7edb6752016-03-21 17:37:52 +0100441 if type(data) is tuple or type(data) is list:
442 for d in data:
sousaedu80135b92021-02-17 15:05:18 +0100443 a = self._remove_extra_items(d, schema["items"])
444 if a is not None:
445 deleted.append(a)
tierno7edb6752016-03-21 17:37:52 +0100446 elif type(data) is dict:
tierno031ded62019-11-14 14:06:31 +0000447 to_delete = []
tierno7edb6752016-03-21 17:37:52 +0100448 for k in data.keys():
sousaedu80135b92021-02-17 15:05:18 +0100449 if "properties" not in schema or k not in schema["properties"].keys():
tierno031ded62019-11-14 14:06:31 +0000450 to_delete.append(k)
tierno7edb6752016-03-21 17:37:52 +0100451 deleted.append(k)
452 else:
sousaedu80135b92021-02-17 15:05:18 +0100453 a = self._remove_extra_items(data[k], schema["properties"][k])
454 if a is not None:
455 deleted.append({k: a})
tierno031ded62019-11-14 14:06:31 +0000456 for k in to_delete:
457 del data[k]
sousaedu80135b92021-02-17 15:05:18 +0100458 if len(deleted) == 0:
459 return None
460 elif len(deleted) == 1:
461 return deleted[0]
tierno7edb6752016-03-21 17:37:52 +0100462 else:
sousaedu80135b92021-02-17 15:05:18 +0100463 return deleted
464
465 def _format_request_exception(self, request_exception):
466 """Transform a request exception into a vimconn exception"""
467 if isinstance(request_exception, js_e.ValidationError):
468 raise vimconn.VimConnUnexpectedResponse(
469 "jsonschema exception '{}' at '{}'".format(
470 request_exception.message, request_exception.path
471 )
472 )
473 elif isinstance(request_exception, requests.exceptions.HTTPError):
474 raise vimconn.VimConnUnexpectedResponse(
475 type(request_exception).__name__ + ": " + str(request_exception)
476 )
477 else:
478 raise vimconn.VimConnConnectionException(
479 type(request_exception).__name__ + ": " + str(request_exception)
480 )
tiernoae4a8d12016-07-08 12:30:39 +0200481
482 def _check_http_request_response(self, request_response):
sousaedu80135b92021-02-17 15:05:18 +0100483 """Raise a vimconn exception if the response is not Ok"""
484 if request_response.status_code >= 200 and request_response.status_code < 300:
tiernoae4a8d12016-07-08 12:30:39 +0200485 return
486 if request_response.status_code == vimconn.HTTP_Unauthorized:
tierno72774862020-05-04 11:44:15 +0000487 raise vimconn.VimConnAuthException(request_response.text)
tiernoae4a8d12016-07-08 12:30:39 +0200488 elif request_response.status_code == vimconn.HTTP_Not_Found:
tierno72774862020-05-04 11:44:15 +0000489 raise vimconn.VimConnNotFoundException(request_response.text)
tiernoae4a8d12016-07-08 12:30:39 +0200490 elif request_response.status_code == vimconn.HTTP_Conflict:
tierno72774862020-05-04 11:44:15 +0000491 raise vimconn.VimConnConflictException(request_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100492 else:
493 raise vimconn.VimConnUnexpectedResponse(
494 "VIM HTTP_response {}, {}".format(
495 request_response.status_code, str(request_response.text)
496 )
497 )
tiernoae4a8d12016-07-08 12:30:39 +0200498
sousaedu80135b92021-02-17 15:05:18 +0100499 def new_tenant(self, tenant_name, tenant_description):
500 """Adds a new tenant to VIM with this name and description, returns the tenant identifier"""
501 # print "VIMConnector: Adding a new tenant to VIM"
502 payload_dict = {
503 "tenant": {
504 "name": tenant_name,
505 "description": tenant_description,
506 "enabled": True,
507 }
508 }
tierno7edb6752016-03-21 17:37:52 +0100509 payload_req = json.dumps(payload_dict)
tierno7edb6752016-03-21 17:37:52 +0100510 try:
sousaedu80135b92021-02-17 15:05:18 +0100511 url = self.url_admin + "/tenants"
tiernoae4a8d12016-07-08 12:30:39 +0200512 self.logger.info("Adding a new tenant %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100513 vim_response = requests.post(
514 url, headers=self.headers_req, data=payload_req
515 )
tiernoae4a8d12016-07-08 12:30:39 +0200516 self._check_http_request_response(vim_response)
517 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100518 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200519 response = vim_response.json()
520 js_v(response, new_tenant_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100521 # r = self._remove_extra_items(response, new_tenant_response_schema)
522 # if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200523 # self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100524 tenant_id = response["tenant"]["id"]
tiernoae4a8d12016-07-08 12:30:39 +0200525 return tenant_id
526 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
527 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100528
sousaedu80135b92021-02-17 15:05:18 +0100529 def delete_tenant(self, tenant_id):
530 """Delete a tenant from VIM. Returns the old tenant identifier"""
tierno7edb6752016-03-21 17:37:52 +0100531 try:
sousaedu80135b92021-02-17 15:05:18 +0100532 url = self.url_admin + "/tenants/" + tenant_id
tiernoae4a8d12016-07-08 12:30:39 +0200533 self.logger.info("Delete a tenant DELETE %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100534 vim_response = requests.delete(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200535 self._check_http_request_response(vim_response)
536 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100537 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200538 return tenant_id
539 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
540 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100541
542 def get_tenant_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +0100543 """Obtain tenants of VIM
tiernoae4a8d12016-07-08 12:30:39 +0200544 filter_dict can contain the following keys:
545 name: filter by tenant name
546 id: filter by tenant uuid/id
547 <other VIM specific>
548 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
sousaedu80135b92021-02-17 15:05:18 +0100549 """
550 filterquery = []
551 filterquery_text = ""
552 for k, v in filter_dict.items():
553 filterquery.append(str(k) + "=" + str(v))
554 if len(filterquery) > 0:
555 filterquery_text = "?" + "&".join(filterquery)
tierno7edb6752016-03-21 17:37:52 +0100556 try:
sousaedu80135b92021-02-17 15:05:18 +0100557 url = self.url + "/tenants" + filterquery_text
tiernoae4a8d12016-07-08 12:30:39 +0200558 self.logger.info("get_tenant_list GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100559 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200560 self._check_http_request_response(vim_response)
561 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100562 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200563 return vim_response.json()["tenants"]
564 except requests.exceptions.RequestException as e:
565 self._format_request_exception(e)
566
sousaedu80135b92021-02-17 15:05:18 +0100567 def new_network(
568 self,
569 net_name,
570 net_type,
571 ip_profile=None,
572 shared=False,
573 provider_network_profile=None,
574 ): # , **vim_specific):
garciadeblasebd66722019-01-31 16:01:31 +0000575 """Adds a tenant network to VIM
576 Params:
577 'net_name': name of the network
578 'net_type': one of:
579 'bridge': overlay isolated network
580 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
581 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
582 'ip_profile': is a dict containing the IP parameters of the network
583 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
584 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
585 'gateway_address': (Optional) ip_schema, that is X.X.X.X
586 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
587 'dhcp_enabled': True or False
588 'dhcp_start_address': ip_schema, first IP to grant
589 'dhcp_count': number of IPs to grant.
590 'shared': if this network can be seen/use by other tenants/organization
kbsuba85c54d2019-10-17 16:30:32 +0000591 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
garciadeblasebd66722019-01-31 16:01:31 +0000592 Returns a tuple with the network identifier and created_items, or raises an exception on error
593 created_items can be None or a dictionary where this method can include key-values that will be passed to
594 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
595 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
596 as not present.
597 """
tiernoae4a8d12016-07-08 12:30:39 +0200598 try:
kbsuba85c54d2019-10-17 16:30:32 +0000599 vlan = None
600 if provider_network_profile:
601 vlan = provider_network_profile.get("segmentation-id")
garciadeblasebd66722019-01-31 16:01:31 +0000602 created_items = {}
tiernoae4a8d12016-07-08 12:30:39 +0200603 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100604 if net_type == "bridge":
605 net_type = "bridge_data"
606 payload_req = {
607 "name": net_name,
608 "type": net_type,
609 "tenant_id": self.tenant,
610 "shared": shared,
611 }
tiernoa7d34d02017-02-23 14:42:07 +0100612 if vlan:
613 payload_req["provider:vlan"] = vlan
614 # payload_req.update(vim_specific)
sousaedu80135b92021-02-17 15:05:18 +0100615 url = self.url + "/networks"
616 self.logger.info(
617 "Adding a new network POST: %s DATA: %s", url, str(payload_req)
618 )
619 vim_response = requests.post(
620 url, headers=self.headers_req, data=json.dumps({"network": payload_req})
621 )
tiernoae4a8d12016-07-08 12:30:39 +0200622 self._check_http_request_response(vim_response)
623 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100624 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200625 response = vim_response.json()
626 js_v(response, new_network_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100627 # r = self._remove_extra_items(response, new_network_response_schema)
628 # if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200629 # self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100630 network_id = response["network"]["id"]
garciadeblasebd66722019-01-31 16:01:31 +0000631 return network_id, created_items
tiernoae4a8d12016-07-08 12:30:39 +0200632 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
633 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +0100634
tierno7edb6752016-03-21 17:37:52 +0100635 def get_network_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +0100636 """Obtain tenant networks of VIM
tierno7edb6752016-03-21 17:37:52 +0100637 Filter_dict can be:
638 name: network name
639 id: network uuid
tiernoae4a8d12016-07-08 12:30:39 +0200640 public: boolean
tierno7edb6752016-03-21 17:37:52 +0100641 tenant_id: tenant
642 admin_state_up: boolean
643 status: 'ACTIVE'
644 Returns the network list of dictionaries
sousaedu80135b92021-02-17 15:05:18 +0100645 """
tierno7edb6752016-03-21 17:37:52 +0100646 try:
sousaedu80135b92021-02-17 15:05:18 +0100647 if "tenant_id" not in filter_dict:
tiernoae4a8d12016-07-08 12:30:39 +0200648 filter_dict["tenant_id"] = self._get_my_tenant()
649 elif not filter_dict["tenant_id"]:
650 del filter_dict["tenant_id"]
sousaedu80135b92021-02-17 15:05:18 +0100651 filterquery = []
652 filterquery_text = ""
653 for k, v in filter_dict.items():
654 filterquery.append(str(k) + "=" + str(v))
655 if len(filterquery) > 0:
656 filterquery_text = "?" + "&".join(filterquery)
657 url = self.url + "/networks" + filterquery_text
tiernoae4a8d12016-07-08 12:30:39 +0200658 self.logger.info("Getting network list GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100659 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200660 self._check_http_request_response(vim_response)
661 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100662 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200663 response = vim_response.json()
sousaedu80135b92021-02-17 15:05:18 +0100664 return response["networks"]
tiernoae4a8d12016-07-08 12:30:39 +0200665 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
666 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100667
tiernoae4a8d12016-07-08 12:30:39 +0200668 def get_network(self, net_id):
sousaedu80135b92021-02-17 15:05:18 +0100669 """Obtain network details of network id"""
tiernoae4a8d12016-07-08 12:30:39 +0200670 try:
sousaedu80135b92021-02-17 15:05:18 +0100671 url = self.url + "/networks/" + net_id
tiernoae4a8d12016-07-08 12:30:39 +0200672 self.logger.info("Getting network GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100673 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200674 self._check_http_request_response(vim_response)
675 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100676 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200677 response = vim_response.json()
sousaedu80135b92021-02-17 15:05:18 +0100678 return response["network"]
tiernoae4a8d12016-07-08 12:30:39 +0200679 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
680 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +0100681
garciadeblasebd66722019-01-31 16:01:31 +0000682 def delete_network(self, net_id, created_items=None):
683 """
684 Removes a tenant network from VIM and its associated elements
685 :param net_id: VIM identifier of the network, provided by method new_network
686 :param created_items: dictionary with extra items to be deleted. provided by method new_network
687 Returns the network identifier or raises an exception upon error or when network is not found
688 """
tierno392f2852016-05-13 12:28:55 +0200689 try:
690 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100691 url = self.url + "/networks/" + net_id
tiernoae4a8d12016-07-08 12:30:39 +0200692 self.logger.info("Deleting VIM network DELETE %s", url)
693 vim_response = requests.delete(url, headers=self.headers_req)
694 self._check_http_request_response(vim_response)
sousaedu80135b92021-02-17 15:05:18 +0100695 # self.logger.debug(vim_response.text)
696 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200697 return net_id
698 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
699 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100700
tiernoae4a8d12016-07-08 12:30:39 +0200701 def get_flavor(self, flavor_id):
sousaedu80135b92021-02-17 15:05:18 +0100702 """Obtain flavor details from the VIM"""
tierno392f2852016-05-13 12:28:55 +0200703 try:
704 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100705 url = self.url + "/" + self.tenant + "/flavors/" + flavor_id
tiernoae4a8d12016-07-08 12:30:39 +0200706 self.logger.info("Getting flavor GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100707 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200708 self._check_http_request_response(vim_response)
709 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100710 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200711 response = vim_response.json()
712 js_v(response, get_flavor_response_schema)
713 r = self._remove_extra_items(response, get_flavor_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100714 if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200715 self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100716 return response["flavor"]
tiernoae4a8d12016-07-08 12:30:39 +0200717 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
718 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +0100719
tiernoae4a8d12016-07-08 12:30:39 +0200720 def new_flavor(self, flavor_data):
sousaedu80135b92021-02-17 15:05:18 +0100721 """Adds a tenant flavor to VIM"""
722 """Returns the flavor identifier"""
tierno392f2852016-05-13 12:28:55 +0200723 try:
tierno7432e7c2017-01-04 16:54:37 +0100724 new_flavor_dict = flavor_data.copy()
sousaedu80135b92021-02-17 15:05:18 +0100725 for device in new_flavor_dict.get("extended", {}).get("devices", ()):
726 if "image name" in device:
727 del device["image name"]
728 if "name" in device:
729 del device["name"]
730 numas = new_flavor_dict.get("extended", {}).get("numas")
tierno66eba6e2017-11-10 17:09:18 +0100731 if numas:
732 numa = numas[0]
733 # translate memory, cpus to EPA
sousaedu80135b92021-02-17 15:05:18 +0100734 if (
735 "cores" not in numa
736 and "threads" not in numa
737 and "paired-threads" not in numa
738 ):
tierno66eba6e2017-11-10 17:09:18 +0100739 numa["paired-threads"] = new_flavor_dict["vcpus"]
740 if "memory" not in numa:
tierno7d782ef2019-10-04 12:56:31 +0000741 numa["memory"] = int(math.ceil(new_flavor_dict["ram"] / 1024.0))
tierno66eba6e2017-11-10 17:09:18 +0100742 for iface in numa.get("interfaces", ()):
743 if not iface.get("bandwidth"):
744 iface["bandwidth"] = "1 Mbps"
745
tierno7432e7c2017-01-04 16:54:37 +0100746 new_flavor_dict["name"] = flavor_data["name"][:64]
tierno392f2852016-05-13 12:28:55 +0200747 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100748 payload_req = json.dumps({"flavor": new_flavor_dict})
749 url = self.url + "/" + self.tenant + "/flavors"
tiernoae4a8d12016-07-08 12:30:39 +0200750 self.logger.info("Adding a new VIM flavor POST %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100751 vim_response = requests.post(
752 url, headers=self.headers_req, data=payload_req
753 )
tiernoae4a8d12016-07-08 12:30:39 +0200754 self._check_http_request_response(vim_response)
755 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100756 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200757 response = vim_response.json()
758 js_v(response, new_flavor_response_schema)
759 r = self._remove_extra_items(response, new_flavor_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100760 if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200761 self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100762 flavor_id = response["flavor"]["id"]
tiernoae4a8d12016-07-08 12:30:39 +0200763 return flavor_id
764 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
765 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100766
sousaedu80135b92021-02-17 15:05:18 +0100767 def delete_flavor(self, flavor_id):
768 """Deletes a tenant flavor from VIM"""
769 """Returns the old flavor_id"""
tierno392f2852016-05-13 12:28:55 +0200770 try:
771 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100772 url = self.url + "/" + self.tenant + "/flavors/" + flavor_id
tiernoae4a8d12016-07-08 12:30:39 +0200773 self.logger.info("Deleting VIM flavor DELETE %s", url)
774 vim_response = requests.delete(url, headers=self.headers_req)
775 self._check_http_request_response(vim_response)
sousaedu80135b92021-02-17 15:05:18 +0100776 # self.logger.debug(vim_response.text)
777 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200778 return flavor_id
779 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
780 self._format_request_exception(e)
781
782 def get_image(self, image_id):
sousaedu80135b92021-02-17 15:05:18 +0100783 """Obtain image details from the VIM"""
tierno7edb6752016-03-21 17:37:52 +0100784 try:
tiernoae4a8d12016-07-08 12:30:39 +0200785 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100786 url = self.url + "/" + self.tenant + "/images/" + image_id
tiernoae4a8d12016-07-08 12:30:39 +0200787 self.logger.info("Getting image GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100788 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +0200789 self._check_http_request_response(vim_response)
790 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100791 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200792 response = vim_response.json()
793 js_v(response, get_image_response_schema)
794 r = self._remove_extra_items(response, get_image_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100795 if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200796 self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100797 return response["image"]
tiernoae4a8d12016-07-08 12:30:39 +0200798 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
799 self._format_request_exception(e)
800
sousaedu80135b92021-02-17 15:05:18 +0100801 def new_image(self, image_dict):
garciadeblasdfad9cd2021-05-14 17:22:01 +0200802 """Adds a tenant image to VIM, returns image_id"""
tiernoae4a8d12016-07-08 12:30:39 +0200803 try:
804 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100805 new_image_dict = {"name": image_dict["name"][:64]}
806 if image_dict.get("description"):
807 new_image_dict["description"] = image_dict["description"]
808 if image_dict.get("metadata"):
809 new_image_dict["metadata"] = yaml.load(
810 image_dict["metadata"], Loader=yaml.SafeLoader
811 )
812 if image_dict.get("location"):
813 new_image_dict["path"] = image_dict["location"]
814 payload_req = json.dumps({"image": new_image_dict})
815 url = self.url + "/" + self.tenant + "/images"
tiernoae4a8d12016-07-08 12:30:39 +0200816 self.logger.info("Adding a new VIM image POST %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100817 vim_response = requests.post(
818 url, headers=self.headers_req, data=payload_req
819 )
tiernoae4a8d12016-07-08 12:30:39 +0200820 self._check_http_request_response(vim_response)
821 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100822 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200823 response = vim_response.json()
824 js_v(response, new_image_response_schema)
825 r = self._remove_extra_items(response, new_image_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100826 if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200827 self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100828 image_id = response["image"]["id"]
tiernoae4a8d12016-07-08 12:30:39 +0200829 return image_id
830 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
831 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +0100832
tiernoae4a8d12016-07-08 12:30:39 +0200833 def delete_image(self, image_id):
sousaedu80135b92021-02-17 15:05:18 +0100834 """Deletes a tenant image from VIM"""
835 """Returns the deleted image_id"""
tiernoae4a8d12016-07-08 12:30:39 +0200836 try:
837 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100838 url = self.url + "/" + self.tenant + "/images/" + image_id
tiernoae4a8d12016-07-08 12:30:39 +0200839 self.logger.info("Deleting VIM image DELETE %s", url)
840 vim_response = requests.delete(url, headers=self.headers_req)
841 self._check_http_request_response(vim_response)
sousaedu80135b92021-02-17 15:05:18 +0100842 # self.logger.debug(vim_response.text)
843 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200844 return image_id
845 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
846 self._format_request_exception(e)
847
tiernoae4a8d12016-07-08 12:30:39 +0200848 def get_image_id_from_path(self, path):
sousaedu80135b92021-02-17 15:05:18 +0100849 """Get the image id from image path in the VIM database. Returns the image_id"""
tiernoae4a8d12016-07-08 12:30:39 +0200850 try:
851 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100852 url = self.url + "/" + self.tenant + "/images?path=" + quote(path)
tiernoae4a8d12016-07-08 12:30:39 +0200853 self.logger.info("Getting images GET %s", url)
854 vim_response = requests.get(url)
855 self._check_http_request_response(vim_response)
856 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100857 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +0200858 response = vim_response.json()
859 js_v(response, get_images_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100860 # r = self._remove_extra_items(response, get_images_response_schema)
861 # if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +0200862 # self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +0100863 if len(response["images"]) == 0:
864 raise vimconn.VimConnNotFoundException(
865 "Image not found at VIM with path '{}'".format(path)
866 )
867 elif len(response["images"]) > 1:
868 raise vimconn.VimConnConflictException(
869 "More than one image found at VIM with path '{}'".format(path)
870 )
871 return response["images"][0]["id"]
tiernoae4a8d12016-07-08 12:30:39 +0200872 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
873 self._format_request_exception(e)
874
garciadeblasb69fa9f2016-09-28 12:04:10 +0200875 def get_image_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +0100876 """Obtain tenant images from VIM
garciadeblasb69fa9f2016-09-28 12:04:10 +0200877 Filter_dict can be:
878 name: image name
879 id: image uuid
880 checksum: image checksum
881 location: image path
882 Returns the image list of dictionaries:
883 [{<the fields at Filter_dict plus some VIM specific>}, ...]
884 List can be empty
sousaedu80135b92021-02-17 15:05:18 +0100885 """
garciadeblasb69fa9f2016-09-28 12:04:10 +0200886 try:
887 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100888 filterquery = []
889 filterquery_text = ""
890 for k, v in filter_dict.items():
891 filterquery.append(str(k) + "=" + str(v))
892 if len(filterquery) > 0:
893 filterquery_text = "?" + "&".join(filterquery)
894 url = self.url + "/" + self.tenant + "/images" + filterquery_text
garciadeblasb69fa9f2016-09-28 12:04:10 +0200895 self.logger.info("Getting image list GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +0100896 vim_response = requests.get(url, headers=self.headers_req)
garciadeblasb69fa9f2016-09-28 12:04:10 +0200897 self._check_http_request_response(vim_response)
898 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +0100899 # print json.dumps(vim_response.json(), indent=4)
garciadeblasb69fa9f2016-09-28 12:04:10 +0200900 response = vim_response.json()
sousaedu80135b92021-02-17 15:05:18 +0100901 return response["images"]
garciadeblasb69fa9f2016-09-28 12:04:10 +0200902 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
903 self._format_request_exception(e)
904
tiernoae4a8d12016-07-08 12:30:39 +0200905 def new_vminstancefromJSON(self, vm_data):
sousaedu80135b92021-02-17 15:05:18 +0100906 """Adds a VM instance to VIM"""
907 """Returns the instance identifier"""
tierno392f2852016-05-13 12:28:55 +0200908 try:
909 self._get_my_tenant()
910 except Exception as e:
911 return -vimconn.HTTP_Not_Found, str(e)
tierno7d782ef2019-10-04 12:56:31 +0000912 print("VIMConnector: Adding a new VM instance from JSON to VIM")
tierno7edb6752016-03-21 17:37:52 +0100913 payload_req = vm_data
914 try:
sousaedu80135b92021-02-17 15:05:18 +0100915 vim_response = requests.post(
916 self.url + "/" + self.tenant + "/servers",
917 headers=self.headers_req,
918 data=payload_req,
919 )
venkatamahesh6ecca182017-01-27 23:04:40 +0530920 except requests.exceptions.RequestException as e:
sousaedu80135b92021-02-17 15:05:18 +0100921 print("new_vminstancefromJSON Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +0100922 return -vimconn.HTTP_Not_Found, str(e.args[0])
tierno7d782ef2019-10-04 12:56:31 +0000923 # print vim_response
sousaedu80135b92021-02-17 15:05:18 +0100924 # print vim_response.status_code
tierno7edb6752016-03-21 17:37:52 +0100925 if vim_response.status_code == 200:
sousaedu80135b92021-02-17 15:05:18 +0100926 # print vim_response.json()
927 # print json.dumps(vim_response.json(), indent=4)
928 res, http_content = self._format_in(vim_response, new_image_response_schema)
929 # print http_content
tierno7edb6752016-03-21 17:37:52 +0100930 if res:
931 r = self._remove_extra_items(http_content, new_image_response_schema)
sousaedu80135b92021-02-17 15:05:18 +0100932 if r is not None:
933 print("Warning: remove extra items ", r)
934 # print http_content
935 vminstance_id = http_content["server"]["id"]
936 print("Tenant image id: ", vminstance_id)
937 return vim_response.status_code, vminstance_id
938 else:
939 return -vimconn.HTTP_Bad_Request, http_content
tierno7edb6752016-03-21 17:37:52 +0100940 else:
sousaedu80135b92021-02-17 15:05:18 +0100941 # print vim_response.text
tierno7edb6752016-03-21 17:37:52 +0100942 jsonerror = self._format_jsonerror(vim_response)
tierno7d782ef2019-10-04 12:56:31 +0000943 text = 'Error in VIM "{}": not possible to add new vm instance. HTTP Response: {}. Error: {}'.format(
sousaedu80135b92021-02-17 15:05:18 +0100944 self.url, vim_response.status_code, jsonerror
945 )
946 # print text
947 return -vim_response.status_code, text
tierno7edb6752016-03-21 17:37:52 +0100948
sousaedu80135b92021-02-17 15:05:18 +0100949 def new_vminstance(
950 self,
951 name,
952 description,
953 start,
954 image_id,
955 flavor_id,
Alexis Romerob70f4ed2022-03-11 18:00:49 +0100956 affinity_group_list,
sousaedu80135b92021-02-17 15:05:18 +0100957 net_list,
958 cloud_config=None,
959 disk_list=None,
960 availability_zone_index=None,
961 availability_zone_list=None,
962 ):
tierno66eba6e2017-11-10 17:09:18 +0100963 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +0100964 Params:
965 start: indicates if VM must start or boot in pause mode. Ignored
966 image_id,flavor_id: image and flavor uuid
967 net_list: list of interfaces, each one is a dictionary with:
968 name:
969 net_id: network uuid to connect
970 vpci: virtual vcpi to assign
garciadeblasc4f4d732018-10-25 18:17:24 +0200971 model: interface model, virtio, e1000, ...
sousaedu80135b92021-02-17 15:05:18 +0100972 mac_address:
tierno7edb6752016-03-21 17:37:52 +0100973 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +0100974 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +0100975 vim_id: filled/added by this function
976 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +0200977 Returns a tuple with the instance identifier and created_items or raises an exception on error
978 created_items can be None or a dictionary where this method can include key-values that will be passed to
979 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
980 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
981 as not present.
tierno66eba6e2017-11-10 17:09:18 +0100982 """
sousaedu80135b92021-02-17 15:05:18 +0100983 self.logger.debug(
984 "new_vminstance input: image='%s' flavor='%s' nics='%s'",
985 image_id,
986 flavor_id,
987 str(net_list),
988 )
tierno392f2852016-05-13 12:28:55 +0200989 try:
990 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +0100991 # net_list = []
992 # for k,v in net_dict.items():
993 # print k,v
994 # net_list.append('{"name":"' + k + '", "uuid":"' + v + '"}')
995 # net_list_string = ', '.join(net_list)
996 virtio_net_list = []
tiernoae4a8d12016-07-08 12:30:39 +0200997 for net in net_list:
tierno7edb6752016-03-21 17:37:52 +0100998 if not net.get("net_id"):
999 continue
sousaedu80135b92021-02-17 15:05:18 +01001000 net_dict = {"uuid": net["net_id"]}
tierno66eba6e2017-11-10 17:09:18 +01001001 if net.get("type"):
1002 if net["type"] == "SR-IOV":
1003 net_dict["type"] = "VF"
1004 elif net["type"] == "PCI-PASSTHROUGH":
1005 net_dict["type"] = "PF"
1006 else:
1007 net_dict["type"] = net["type"]
1008 if net.get("name"):
1009 net_dict["name"] = net["name"]
1010 if net.get("vpci"):
1011 net_dict["vpci"] = net["vpci"]
1012 if net.get("model"):
garciadeblas31e141b2018-10-25 18:33:19 +02001013 if net["model"] == "VIRTIO" or net["model"] == "paravirt":
tierno66eba6e2017-11-10 17:09:18 +01001014 net_dict["model"] = "virtio"
1015 else:
1016 net_dict["model"] = net["model"]
1017 if net.get("mac_address"):
1018 net_dict["mac_address"] = net["mac_address"]
tierno41a69812018-02-16 14:34:33 +01001019 if net.get("ip_address"):
1020 net_dict["ip_address"] = net["ip_address"]
tiernoae4a8d12016-07-08 12:30:39 +02001021 virtio_net_list.append(net_dict)
sousaedu80135b92021-02-17 15:05:18 +01001022 payload_dict = {
1023 "name": name[:64],
1024 "description": description,
1025 "imageRef": image_id,
1026 "flavorRef": flavor_id,
1027 "networks": virtio_net_list,
1028 }
1029 if start is not None:
tiernoae4a8d12016-07-08 12:30:39 +02001030 payload_dict["start"] = start
1031 payload_req = json.dumps({"server": payload_dict})
sousaedu80135b92021-02-17 15:05:18 +01001032 url = self.url + "/" + self.tenant + "/servers"
tiernoae4a8d12016-07-08 12:30:39 +02001033 self.logger.info("Adding a new vm POST %s DATA %s", url, payload_req)
sousaedu80135b92021-02-17 15:05:18 +01001034 vim_response = requests.post(
1035 url, headers=self.headers_req, data=payload_req
1036 )
tiernoae4a8d12016-07-08 12:30:39 +02001037 self._check_http_request_response(vim_response)
1038 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +01001039 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +02001040 response = vim_response.json()
1041 js_v(response, new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001042 # r = self._remove_extra_items(response, new_vminstance_response_schema)
1043 # if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +02001044 # self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +01001045 vminstance_id = response["server"]["id"]
tiernoae4a8d12016-07-08 12:30:39 +02001046
sousaedu80135b92021-02-17 15:05:18 +01001047 # connect data plane interfaces to network
tiernoae4a8d12016-07-08 12:30:39 +02001048 for net in net_list:
sousaedu80135b92021-02-17 15:05:18 +01001049 if net["type"] == "virtual":
tiernoae4a8d12016-07-08 12:30:39 +02001050 if not net.get("net_id"):
1051 continue
sousaedu80135b92021-02-17 15:05:18 +01001052 for iface in response["server"]["networks"]:
tiernoae4a8d12016-07-08 12:30:39 +02001053 if "name" in net:
sousaedu80135b92021-02-17 15:05:18 +01001054 if net["name"] == iface["name"]:
1055 net["vim_id"] = iface["iface_id"]
tiernoae4a8d12016-07-08 12:30:39 +02001056 break
1057 elif "net_id" in net:
sousaedu80135b92021-02-17 15:05:18 +01001058 if net["net_id"] == iface["net_id"]:
1059 net["vim_id"] = iface["iface_id"]
tiernoae4a8d12016-07-08 12:30:39 +02001060 break
sousaedu80135b92021-02-17 15:05:18 +01001061 else: # dataplane
1062 for numa in response["server"].get("extended", {}).get("numas", ()):
1063 for iface in numa.get("interfaces", ()):
1064 if net["name"] == iface["name"]:
1065 net["vim_id"] = iface["iface_id"]
1066 # Code bellow is not needed, current openvim connect dataplane interfaces
1067 # if net.get("net_id"):
1068 # connect dataplane interface
tiernoae4a8d12016-07-08 12:30:39 +02001069 # result, port_id = self.connect_port_network(iface['iface_id'], net["net_id"])
1070 # if result < 0:
sousaedu80135b92021-02-17 15:05:18 +01001071 # error_text = "Error attaching port %s to network %s: %s." % (iface['iface_id']
1072 # , net["net_id"], port_id)
tiernoae4a8d12016-07-08 12:30:39 +02001073 # print "new_vminstance: " + error_text
1074 # self.delete_vminstance(vminstance_id)
1075 # return result, error_text
1076 break
sousaedu80135b92021-02-17 15:05:18 +01001077
tierno98e909c2017-10-14 13:27:03 +02001078 return vminstance_id, None
tiernoae4a8d12016-07-08 12:30:39 +02001079 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1080 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001081
tiernoae4a8d12016-07-08 12:30:39 +02001082 def get_vminstance(self, vm_id):
sousaedu80135b92021-02-17 15:05:18 +01001083 """Returns the VM instance information from VIM"""
tierno392f2852016-05-13 12:28:55 +02001084 try:
1085 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +01001086 url = self.url + "/" + self.tenant + "/servers/" + vm_id
tiernoae4a8d12016-07-08 12:30:39 +02001087 self.logger.info("Getting vm GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +01001088 vim_response = requests.get(url, headers=self.headers_req)
1089 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +02001090 self._check_http_request_response(vim_response)
1091 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +01001092 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +02001093 response = vim_response.json()
1094 js_v(response, new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001095 # r = self._remove_extra_items(response, new_vminstance_response_schema)
1096 # if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +02001097 # self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +01001098 return response["server"]
tiernoae4a8d12016-07-08 12:30:39 +02001099 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1100 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001101
tierno98e909c2017-10-14 13:27:03 +02001102 def delete_vminstance(self, vm_id, created_items=None):
sousaedu80135b92021-02-17 15:05:18 +01001103 """Removes a VM instance from VIM, returns the deleted vm_id"""
tierno392f2852016-05-13 12:28:55 +02001104 try:
1105 self._get_my_tenant()
sousaedu80135b92021-02-17 15:05:18 +01001106 url = self.url + "/" + self.tenant + "/servers/" + vm_id
tiernoae4a8d12016-07-08 12:30:39 +02001107 self.logger.info("Deleting VIM vm DELETE %s", url)
1108 vim_response = requests.delete(url, headers=self.headers_req)
1109 self._check_http_request_response(vim_response)
sousaedu80135b92021-02-17 15:05:18 +01001110 # self.logger.debug(vim_response.text)
1111 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +02001112 return vm_id
1113 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1114 self._format_request_exception(e)
1115
1116 def refresh_vms_status(self, vm_list):
sousaedu80135b92021-02-17 15:05:18 +01001117 """Refreshes the status of the virtual machines"""
tierno7edb6752016-03-21 17:37:52 +01001118 try:
tiernoae4a8d12016-07-08 12:30:39 +02001119 self._get_my_tenant()
1120 except requests.exceptions.RequestException as e:
1121 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001122 vm_dict = {}
tiernoae4a8d12016-07-08 12:30:39 +02001123 for vm_id in vm_list:
sousaedu80135b92021-02-17 15:05:18 +01001124 vm = {}
1125 # print "VIMConnector refresh_tenant_vms and nets: Getting tenant VM instance information from VIM"
tiernoae4a8d12016-07-08 12:30:39 +02001126 try:
sousaedu80135b92021-02-17 15:05:18 +01001127 url = self.url + "/" + self.tenant + "/servers/" + vm_id
tiernoae4a8d12016-07-08 12:30:39 +02001128 self.logger.info("Getting vm GET %s", url)
sousaedu80135b92021-02-17 15:05:18 +01001129 vim_response = requests.get(url, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +02001130 self._check_http_request_response(vim_response)
1131 response = vim_response.json()
1132 js_v(response, new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001133 if response["server"]["status"] in vmStatus2manoFormat:
1134 vm["status"] = vmStatus2manoFormat[response["server"]["status"]]
tiernoae4a8d12016-07-08 12:30:39 +02001135 else:
sousaedu80135b92021-02-17 15:05:18 +01001136 vm["status"] = "OTHER"
1137 vm["error_msg"] = (
1138 "VIM status reported " + response["server"]["status"]
1139 )
1140 if response["server"].get("last_error"):
1141 vm["error_msg"] = response["server"]["last_error"]
1142 vm["vim_info"] = yaml.safe_dump(response["server"])
1143 # get interfaces info
tiernoae4a8d12016-07-08 12:30:39 +02001144 try:
1145 management_ip = False
sousaedu80135b92021-02-17 15:05:18 +01001146 url2 = self.url + "/ports?device_id=" + quote(vm_id)
tiernoae4a8d12016-07-08 12:30:39 +02001147 self.logger.info("Getting PORTS GET %s", url2)
sousaedu80135b92021-02-17 15:05:18 +01001148 vim_response2 = requests.get(url2, headers=self.headers_req)
tiernoae4a8d12016-07-08 12:30:39 +02001149 self._check_http_request_response(vim_response2)
1150 client_data = vim_response2.json()
1151 if isinstance(client_data.get("ports"), list):
sousaedu80135b92021-02-17 15:05:18 +01001152 vm["interfaces"] = []
tiernoae4a8d12016-07-08 12:30:39 +02001153 for port in client_data.get("ports"):
sousaedu80135b92021-02-17 15:05:18 +01001154 interface = {}
1155 interface["vim_info"] = yaml.safe_dump(port)
tiernoae4a8d12016-07-08 12:30:39 +02001156 interface["mac_address"] = port.get("mac_address")
tierno8e995ce2016-09-22 08:13:00 +00001157 interface["vim_net_id"] = port.get("network_id")
tiernoae4a8d12016-07-08 12:30:39 +02001158 interface["vim_interface_id"] = port["id"]
1159 interface["ip_address"] = port.get("ip_address")
1160 if interface["ip_address"]:
1161 management_ip = True
1162 if interface["ip_address"] == "0.0.0.0":
1163 interface["ip_address"] = None
1164 vm["interfaces"].append(interface)
tierno7edb6752016-03-21 17:37:52 +01001165
sousaedu80135b92021-02-17 15:05:18 +01001166 except Exception as e:
1167 self.logger.error(
1168 "refresh_vms_and_nets. Port get %s: %s",
1169 type(e).__name__,
1170 str(e),
1171 )
1172
1173 if vm["status"] == "ACTIVE" and not management_ip:
1174 vm["status"] = "ACTIVE:NoMgmtIP"
1175
tierno72774862020-05-04 11:44:15 +00001176 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001177 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001178 vm["status"] = "DELETED"
1179 vm["error_msg"] = str(e)
1180 except (
1181 requests.exceptions.RequestException,
1182 js_e.ValidationError,
1183 vimconn.VimConnException,
1184 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001185 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001186 vm["status"] = "VIM_ERROR"
1187 vm["error_msg"] = str(e)
tiernoae4a8d12016-07-08 12:30:39 +02001188 vm_dict[vm_id] = vm
1189 return vm_dict
tierno7edb6752016-03-21 17:37:52 +01001190
tiernoae4a8d12016-07-08 12:30:39 +02001191 def refresh_nets_status(self, net_list):
sousaedu80135b92021-02-17 15:05:18 +01001192 """Get the status of the networks
1193 Params: the list of network identifiers
1194 Returns a dictionary with:
1195 net_id: #VIM id of this network
1196 status: #Mandatory. Text with one of:
1197 # DELETED (not found at vim)
1198 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1199 # OTHER (Vim reported other status not understood)
1200 # ERROR (VIM indicates an ERROR status)
1201 # ACTIVE, INACTIVE, DOWN (admin down),
1202 # BUILD (on building process)
1203 #
1204 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1205 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tiernoae4a8d12016-07-08 12:30:39 +02001206
sousaedu80135b92021-02-17 15:05:18 +01001207 """
tierno392f2852016-05-13 12:28:55 +02001208 try:
1209 self._get_my_tenant()
tiernoae4a8d12016-07-08 12:30:39 +02001210 except requests.exceptions.RequestException as e:
1211 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001212
1213 net_dict = {}
tiernoae4a8d12016-07-08 12:30:39 +02001214 for net_id in net_list:
1215 net = {}
sousaedu80135b92021-02-17 15:05:18 +01001216 # print "VIMConnector refresh_tenant_vms_and_nets:
1217 # Getting tenant network from VIM (tenant: " + str(self.tenant) + "): "
tierno7edb6752016-03-21 17:37:52 +01001218 try:
tiernoae4a8d12016-07-08 12:30:39 +02001219 net_vim = self.get_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001220 if net_vim["status"] in netStatus2manoFormat:
1221 net["status"] = netStatus2manoFormat[net_vim["status"]]
tierno7edb6752016-03-21 17:37:52 +01001222 else:
tiernoae4a8d12016-07-08 12:30:39 +02001223 net["status"] = "OTHER"
sousaedu80135b92021-02-17 15:05:18 +01001224 net["error_msg"] = "VIM status reported " + net_vim["status"]
1225
1226 if net["status"] == "ACTIVE" and not net_vim["admin_state_up"]:
tiernoae4a8d12016-07-08 12:30:39 +02001227 net["status"] = "DOWN"
sousaedu80135b92021-02-17 15:05:18 +01001228 if net_vim.get("last_error"):
1229 net["error_msg"] = net_vim["last_error"]
tiernoae4a8d12016-07-08 12:30:39 +02001230 net["vim_info"] = yaml.safe_dump(net_vim)
tierno72774862020-05-04 11:44:15 +00001231 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001232 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001233 net["status"] = "DELETED"
1234 net["error_msg"] = str(e)
1235 except (
1236 requests.exceptions.RequestException,
1237 js_e.ValidationError,
1238 vimconn.VimConnException,
1239 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001240 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001241 net["status"] = "VIM_ERROR"
1242 net["error_msg"] = str(e)
tiernoae4a8d12016-07-08 12:30:39 +02001243 net_dict[net_id] = net
1244 return net_dict
sousaedu80135b92021-02-17 15:05:18 +01001245
tierno98e909c2017-10-14 13:27:03 +02001246 def action_vminstance(self, vm_id, action_dict, created_items={}):
sousaedu80135b92021-02-17 15:05:18 +01001247 """Send and action over a VM instance from VIM"""
1248 """Returns the status"""
tierno392f2852016-05-13 12:28:55 +02001249 try:
1250 self._get_my_tenant()
tierno7edb6752016-03-21 17:37:52 +01001251 if "console" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01001252 raise vimconn.VimConnException(
1253 "getting console is not available at openvim",
1254 http_code=vimconn.HTTP_Service_Unavailable,
1255 )
1256 url = self.url + "/" + self.tenant + "/servers/" + vm_id + "/action"
tiernoae4a8d12016-07-08 12:30:39 +02001257 self.logger.info("Action over VM instance POST %s", url)
sousaedu80135b92021-02-17 15:05:18 +01001258 vim_response = requests.post(
1259 url, headers=self.headers_req, data=json.dumps(action_dict)
1260 )
tiernoae4a8d12016-07-08 12:30:39 +02001261 self._check_http_request_response(vim_response)
tierno98e909c2017-10-14 13:27:03 +02001262 return None
tiernoae4a8d12016-07-08 12:30:39 +02001263 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1264 self._format_request_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001265
sousaedu80135b92021-02-17 15:05:18 +01001266 # NOT USED METHODS in current version
1267
tierno7edb6752016-03-21 17:37:52 +01001268 def host_vim2gui(self, host, server_dict):
sousaedu80135b92021-02-17 15:05:18 +01001269 """Transform host dictionary from VIM format to GUI format,
tierno7edb6752016-03-21 17:37:52 +01001270 and append to the server_dict
sousaedu80135b92021-02-17 15:05:18 +01001271 """
1272 if type(server_dict) is not dict:
1273 print(
1274 "vimconnector.host_vim2gui() ERROR, param server_dict must be a dictionary"
1275 )
tierno7edb6752016-03-21 17:37:52 +01001276 return
sousaedu80135b92021-02-17 15:05:18 +01001277 RAD = {}
1278 occupation = {}
1279 for numa in host["host"]["numas"]:
1280 RAD_item = {}
1281 occupation_item = {}
1282 # memory
1283 RAD_item["memory"] = {
1284 "size": str(numa["memory"]) + "GB",
1285 "eligible": str(numa["hugepages"]) + "GB",
1286 }
1287 occupation_item["memory"] = str(numa["hugepages_consumed"]) + "GB"
1288 # cpus
1289 RAD_item["cpus"] = {}
1290 RAD_item["cpus"]["cores"] = []
1291 RAD_item["cpus"]["eligible_cores"] = []
1292 occupation_item["cores"] = []
1293 for _ in range(0, len(numa["cores"]) // 2):
1294 RAD_item["cpus"]["cores"].append([])
1295 for core in numa["cores"]:
1296 RAD_item["cpus"]["cores"][core["core_id"]].append(core["thread_id"])
1297 if "status" not in core:
1298 RAD_item["cpus"]["eligible_cores"].append(core["thread_id"])
1299 if "instance_id" in core:
1300 occupation_item["cores"].append(core["thread_id"])
1301 # ports
1302 RAD_item["ports"] = {}
1303 occupation_item["ports"] = {}
1304 for iface in numa["interfaces"]:
1305 RAD_item["ports"][iface["pci"]] = "speed:" + str(iface["Mbps"]) + "M"
1306 occupation_item["ports"][iface["pci"]] = {
1307 "occupied": str(100 * iface["Mbps_consumed"] // iface["Mbps"]) + "%"
1308 }
1309
1310 RAD[numa["numa_socket"]] = RAD_item
1311 occupation[numa["numa_socket"]] = occupation_item
1312 server_dict[host["host"]["name"]] = {"RAD": RAD, "occupation": occupation}
tierno7edb6752016-03-21 17:37:52 +01001313
1314 def get_hosts_info(self):
sousaedu80135b92021-02-17 15:05:18 +01001315 """Get the information of deployed hosts
1316 Returns the hosts content"""
1317 # obtain hosts list
1318 url = self.url + "/hosts"
tierno7edb6752016-03-21 17:37:52 +01001319 try:
1320 vim_response = requests.get(url)
venkatamahesh6ecca182017-01-27 23:04:40 +05301321 except requests.exceptions.RequestException as e:
sousaedu80135b92021-02-17 15:05:18 +01001322 print("get_hosts_info Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +01001323 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001324 print(
1325 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1326 )
1327 # print vim_response.status_code
1328 # print json.dumps(vim_response.json(), indent=4)
tierno7edb6752016-03-21 17:37:52 +01001329 if vim_response.status_code != 200:
tierno7d782ef2019-10-04 12:56:31 +00001330 # TODO: get error
sousaedu80135b92021-02-17 15:05:18 +01001331 print(
1332 "vimconnector.get_hosts_info error getting host list {} {}".format(
1333 vim_response.status_code, vim_response.json()
1334 )
1335 )
tierno7edb6752016-03-21 17:37:52 +01001336 return -vim_response.status_code, "Error getting host list"
sousaedu80135b92021-02-17 15:05:18 +01001337
1338 res, hosts = self._format_in(vim_response, get_hosts_response_schema)
1339
1340 if not res:
1341 print(
1342 "vimconnector.get_hosts_info error parsing GET HOSTS vim response",
1343 hosts,
1344 )
tierno7edb6752016-03-21 17:37:52 +01001345 return vimconn.HTTP_Internal_Server_Error, hosts
sousaedu80135b92021-02-17 15:05:18 +01001346 # obtain hosts details
1347 hosts_dict = {}
1348 for host in hosts["hosts"]:
1349 url = self.url + "/hosts/" + host["id"]
tierno7edb6752016-03-21 17:37:52 +01001350 try:
1351 vim_response = requests.get(url)
venkatamahesh6ecca182017-01-27 23:04:40 +05301352 except requests.exceptions.RequestException as e:
sousaedu80135b92021-02-17 15:05:18 +01001353 print("get_hosts_info Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +01001354 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001355 print(
1356 "vim get",
1357 url,
1358 "response:",
1359 vim_response.status_code,
1360 vim_response.json(),
1361 )
tierno7edb6752016-03-21 17:37:52 +01001362 if vim_response.status_code != 200:
sousaedu80135b92021-02-17 15:05:18 +01001363 print(
1364 "vimconnector.get_hosts_info error getting detailed host {} {}".format(
1365 vim_response.status_code, vim_response.json()
1366 )
1367 )
tierno7edb6752016-03-21 17:37:52 +01001368 continue
sousaedu80135b92021-02-17 15:05:18 +01001369 res, host_detail = self._format_in(
1370 vim_response, get_host_detail_response_schema
1371 )
1372 if not res:
1373 print(
1374 "vimconnector.get_hosts_info error parsing GET HOSTS/{} vim response {}".format(
1375 host["id"], host_detail
1376 ),
1377 )
tierno7edb6752016-03-21 17:37:52 +01001378 continue
sousaedu80135b92021-02-17 15:05:18 +01001379 # print 'host id '+host['id'], json.dumps(host_detail, indent=4)
tierno7edb6752016-03-21 17:37:52 +01001380 self.host_vim2gui(host_detail, hosts_dict)
1381 return 200, hosts_dict
1382
1383 def get_hosts(self, vim_tenant):
sousaedu80135b92021-02-17 15:05:18 +01001384 """Get the hosts and deployed instances
1385 Returns the hosts content"""
1386 # obtain hosts list
1387 url = self.url + "/hosts"
tierno7edb6752016-03-21 17:37:52 +01001388 try:
1389 vim_response = requests.get(url)
venkatamahesh6ecca182017-01-27 23:04:40 +05301390 except requests.exceptions.RequestException as e:
tierno7d782ef2019-10-04 12:56:31 +00001391 print("get_hosts Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +01001392 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001393 print(
1394 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1395 )
1396 # print vim_response.status_code
1397 # print json.dumps(vim_response.json(), indent=4)
tierno7edb6752016-03-21 17:37:52 +01001398 if vim_response.status_code != 200:
sousaedu80135b92021-02-17 15:05:18 +01001399 # TODO: get error
1400 print(
1401 "vimconnector.get_hosts error getting host list {} {}".format(
1402 vim_response.status_code, vim_response.json()
1403 )
1404 )
tierno7edb6752016-03-21 17:37:52 +01001405 return -vim_response.status_code, "Error getting host list"
sousaedu80135b92021-02-17 15:05:18 +01001406
1407 res, hosts = self._format_in(vim_response, get_hosts_response_schema)
1408
1409 if not res:
tierno7d782ef2019-10-04 12:56:31 +00001410 print("vimconnector.get_host error parsing GET HOSTS vim response", hosts)
tierno7edb6752016-03-21 17:37:52 +01001411 return vimconn.HTTP_Internal_Server_Error, hosts
sousaedu80135b92021-02-17 15:05:18 +01001412 # obtain instances from hosts
1413 for host in hosts["hosts"]:
1414 url = self.url + "/" + vim_tenant + "/servers?hostId=" + host["id"]
tierno7edb6752016-03-21 17:37:52 +01001415 try:
1416 vim_response = requests.get(url)
venkatamahesh6ecca182017-01-27 23:04:40 +05301417 except requests.exceptions.RequestException as e:
tierno7d782ef2019-10-04 12:56:31 +00001418 print("get_hosts Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +01001419 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001420 print(
1421 "vim get",
1422 url,
1423 "response:",
1424 vim_response.status_code,
1425 vim_response.json(),
1426 )
tierno7edb6752016-03-21 17:37:52 +01001427 if vim_response.status_code != 200:
sousaedu80135b92021-02-17 15:05:18 +01001428 print(
1429 "vimconnector.get_hosts error getting instances at host {} {}".format(
1430 vim_response.status_code, vim_response.json()
1431 )
1432 )
tierno7edb6752016-03-21 17:37:52 +01001433 continue
sousaedu80135b92021-02-17 15:05:18 +01001434 res, servers = self._format_in(vim_response, get_server_response_schema)
1435 if not res:
1436 print(
1437 "vimconnector.get_host error parsing GET SERVERS/{} vim response {}".format(
1438 host["id"], servers
1439 ),
1440 )
tierno7edb6752016-03-21 17:37:52 +01001441 continue
sousaedu80135b92021-02-17 15:05:18 +01001442 # print 'host id '+host['id'], json.dumps(host_detail, indent=4)
1443 host["instances"] = servers["servers"]
1444 return 200, hosts["hosts"]
tierno7edb6752016-03-21 17:37:52 +01001445
1446 def get_processor_rankings(self):
sousaedu80135b92021-02-17 15:05:18 +01001447 """Get the processor rankings in the VIM database"""
1448 url = self.url + "/processor_ranking"
tierno7edb6752016-03-21 17:37:52 +01001449 try:
1450 vim_response = requests.get(url)
venkatamahesh6ecca182017-01-27 23:04:40 +05301451 except requests.exceptions.RequestException as e:
tierno7d782ef2019-10-04 12:56:31 +00001452 print("get_processor_rankings Exception: ", e.args)
tierno7edb6752016-03-21 17:37:52 +01001453 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001454 print(
1455 "vim get", url, "response:", vim_response.status_code, vim_response.json()
1456 )
1457 # print vim_response.status_code
1458 # print json.dumps(vim_response.json(), indent=4)
tierno7edb6752016-03-21 17:37:52 +01001459 if vim_response.status_code != 200:
sousaedu80135b92021-02-17 15:05:18 +01001460 # TODO: get error
1461 print(
1462 "vimconnector.get_processor_rankings error getting processor rankings {} {}".format(
1463 vim_response.status_code, vim_response.json()
1464 )
1465 )
tierno7edb6752016-03-21 17:37:52 +01001466 return -vim_response.status_code, "Error getting processor rankings"
sousaedu80135b92021-02-17 15:05:18 +01001467
1468 res, rankings = self._format_in(
1469 vim_response, get_processor_rankings_response_schema
1470 )
1471 return res, rankings["rankings"]
1472
tiernoae4a8d12016-07-08 12:30:39 +02001473 def new_host(self, host_data):
sousaedu80135b92021-02-17 15:05:18 +01001474 """Adds a new host to VIM"""
1475 """Returns status code of the VIM response"""
tiernoae4a8d12016-07-08 12:30:39 +02001476 payload_req = host_data
tierno392f2852016-05-13 12:28:55 +02001477 try:
sousaedu80135b92021-02-17 15:05:18 +01001478 url = self.url_admin + "/hosts"
tiernoae4a8d12016-07-08 12:30:39 +02001479 self.logger.info("Adding a new host POST %s", url)
sousaedu80135b92021-02-17 15:05:18 +01001480 vim_response = requests.post(
1481 url, headers=self.headers_req, data=payload_req
1482 )
tiernoae4a8d12016-07-08 12:30:39 +02001483 self._check_http_request_response(vim_response)
1484 self.logger.debug(vim_response.text)
sousaedu80135b92021-02-17 15:05:18 +01001485 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +02001486 response = vim_response.json()
1487 js_v(response, new_host_response_schema)
1488 r = self._remove_extra_items(response, new_host_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001489 if r is not None:
tiernoae4a8d12016-07-08 12:30:39 +02001490 self.logger.warn("Warning: remove extra items %s", str(r))
sousaedu80135b92021-02-17 15:05:18 +01001491 host_id = response["host"]["id"]
tiernoae4a8d12016-07-08 12:30:39 +02001492 return host_id
1493 except (requests.exceptions.RequestException, js_e.ValidationError) as e:
1494 self._format_request_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001495
tiernoae4a8d12016-07-08 12:30:39 +02001496 def new_external_port(self, port_data):
sousaedu80135b92021-02-17 15:05:18 +01001497 """Adds a external port to VIM"""
1498 """Returns the port identifier"""
1499 # TODO change to logging exception code policies
1500 print("VIMConnector: Adding a new external port")
tiernoae4a8d12016-07-08 12:30:39 +02001501 payload_req = port_data
tierno7edb6752016-03-21 17:37:52 +01001502 try:
sousaedu80135b92021-02-17 15:05:18 +01001503 vim_response = requests.post(
1504 self.url_admin + "/ports", headers=self.headers_req, data=payload_req
1505 )
venkatamahesh6ecca182017-01-27 23:04:40 +05301506 except requests.exceptions.RequestException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001507 self.logger.error("new_external_port Exception: ", str(e))
tierno7edb6752016-03-21 17:37:52 +01001508 return -vimconn.HTTP_Not_Found, str(e.args[0])
sousaedu80135b92021-02-17 15:05:18 +01001509 print(vim_response)
1510 # print vim_response.status_code
tiernoae4a8d12016-07-08 12:30:39 +02001511 if vim_response.status_code == 200:
sousaedu80135b92021-02-17 15:05:18 +01001512 # print vim_response.json()
1513 # print json.dumps(vim_response.json(), indent=4)
tiernoae4a8d12016-07-08 12:30:39 +02001514 res, http_content = self._format_in(vim_response, new_port_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001515 # print http_content
tiernoae4a8d12016-07-08 12:30:39 +02001516 if res:
1517 r = self._remove_extra_items(http_content, new_port_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001518 if r is not None:
1519 print("Warning: remove extra items ", r)
1520 # print http_content
1521 port_id = http_content["port"]["id"]
1522 print("Port id: ", port_id)
1523 return vim_response.status_code, port_id
1524 else:
1525 return -vimconn.HTTP_Bad_Request, http_content
tiernoae4a8d12016-07-08 12:30:39 +02001526 else:
sousaedu80135b92021-02-17 15:05:18 +01001527 # print vim_response.text
tiernoae4a8d12016-07-08 12:30:39 +02001528 jsonerror = self._format_jsonerror(vim_response)
tierno7d782ef2019-10-04 12:56:31 +00001529 text = 'Error in VIM "{}": not possible to add new external port. HTTP Response: {}. Error: {}'.format(
sousaedu80135b92021-02-17 15:05:18 +01001530 self.url_admin, vim_response.status_code, jsonerror
1531 )
1532 # print text
1533 return -vim_response.status_code, text
1534
1535 def new_external_network(self, net_name, net_type):
1536 """Adds a external network to VIM (shared)"""
1537 """Returns the network identifier"""
1538 # TODO change to logging exception code policies
1539 print(
1540 "VIMConnector: Adding external shared network to VIM (type "
1541 + net_type
1542 + "): "
1543 + net_name
1544 )
1545
1546 payload_req = (
1547 '{"network":{"name": "'
1548 + net_name
1549 + '","shared":true,"type": "'
1550 + net_type
1551 + '"}}'
1552 )
tiernoae4a8d12016-07-08 12:30:39 +02001553 try:
sousaedu80135b92021-02-17 15:05:18 +01001554 vim_response = requests.post(
1555 self.url + "/networks", headers=self.headers_req, data=payload_req
1556 )
venkatamahesh6ecca182017-01-27 23:04:40 +05301557 except requests.exceptions.RequestException as e:
sousaedu80135b92021-02-17 15:05:18 +01001558 self.logger.error("new_external_network Exception: ", e.args)
tiernoae4a8d12016-07-08 12:30:39 +02001559 return -vimconn.HTTP_Not_Found, str(e.args[0])
tierno7d782ef2019-10-04 12:56:31 +00001560 print(vim_response)
sousaedu80135b92021-02-17 15:05:18 +01001561 # print vim_response.status_code
tiernoae4a8d12016-07-08 12:30:39 +02001562 if vim_response.status_code == 200:
sousaedu80135b92021-02-17 15:05:18 +01001563 # print vim_response.json()
1564 # print json.dumps(vim_response.json(), indent=4)
1565 res, http_content = self._format_in(
1566 vim_response, new_network_response_schema
1567 )
1568 # print http_content
tiernoae4a8d12016-07-08 12:30:39 +02001569 if res:
1570 r = self._remove_extra_items(http_content, new_network_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001571 if r is not None:
1572 print("Warning: remove extra items ", r)
1573 # print http_content
1574 network_id = http_content["network"]["id"]
1575 print("Network id: ", network_id)
1576 return vim_response.status_code, network_id
1577 else:
1578 return -vimconn.HTTP_Bad_Request, http_content
tiernoae4a8d12016-07-08 12:30:39 +02001579 else:
sousaedu80135b92021-02-17 15:05:18 +01001580 # print vim_response.text
tiernoae4a8d12016-07-08 12:30:39 +02001581 jsonerror = self._format_jsonerror(vim_response)
tierno7d782ef2019-10-04 12:56:31 +00001582 text = 'Error in VIM "{}": not possible to add new external network. HTTP Response: {}. Error: {}'.format(
sousaedu80135b92021-02-17 15:05:18 +01001583 self.url, vim_response.status_code, jsonerror
1584 )
1585 # print text
1586 return -vim_response.status_code, text
1587
tiernoae4a8d12016-07-08 12:30:39 +02001588 def connect_port_network(self, port_id, network_id, admin=False):
sousaedu80135b92021-02-17 15:05:18 +01001589 """Connects a external port to a network"""
1590 """Returns status code of the VIM response"""
1591 # TODO change to logging exception code policies
tierno7d782ef2019-10-04 12:56:31 +00001592 print("VIMConnector: Connecting external port to network")
sousaedu80135b92021-02-17 15:05:18 +01001593
tiernoae4a8d12016-07-08 12:30:39 +02001594 payload_req = '{"port":{"network_id":"' + network_id + '"}}'
1595 if admin:
sousaedu80135b92021-02-17 15:05:18 +01001596 if self.url_admin is None:
1597 return (
1598 -vimconn.HTTP_Unauthorized,
1599 "datacenter cannot contain admin URL",
1600 )
1601 url = self.url_admin
tiernoae4a8d12016-07-08 12:30:39 +02001602 else:
sousaedu80135b92021-02-17 15:05:18 +01001603 url = self.url
tiernoae4a8d12016-07-08 12:30:39 +02001604 try:
sousaedu80135b92021-02-17 15:05:18 +01001605 vim_response = requests.put(
1606 url + "/ports/" + port_id, headers=self.headers_req, data=payload_req
1607 )
venkatamahesh6ecca182017-01-27 23:04:40 +05301608 except requests.exceptions.RequestException as e:
tierno7d782ef2019-10-04 12:56:31 +00001609 print("connect_port_network Exception: ", e.args)
tiernoae4a8d12016-07-08 12:30:39 +02001610 return -vimconn.HTTP_Not_Found, str(e.args[0])
tierno7d782ef2019-10-04 12:56:31 +00001611 print(vim_response)
sousaedu80135b92021-02-17 15:05:18 +01001612 # print vim_response.status_code
tiernoae4a8d12016-07-08 12:30:39 +02001613 if vim_response.status_code == 200:
sousaedu80135b92021-02-17 15:05:18 +01001614 # print vim_response.json()
1615 # print json.dumps(vim_response.json(), indent=4)
1616 res, http_content = self._format_in(vim_response, new_port_response_schema)
1617 # print http_content
tiernoae4a8d12016-07-08 12:30:39 +02001618 if res:
1619 r = self._remove_extra_items(http_content, new_port_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001620 if r is not None:
1621 print("Warning: remove extra items ", r)
1622 # print http_content
1623 port_id = http_content["port"]["id"]
1624 print("Port id: ", port_id)
1625 return vim_response.status_code, port_id
1626 else:
1627 return -vimconn.HTTP_Bad_Request, http_content
tiernoae4a8d12016-07-08 12:30:39 +02001628 else:
tierno7d782ef2019-10-04 12:56:31 +00001629 print(vim_response.text)
tiernoae4a8d12016-07-08 12:30:39 +02001630 jsonerror = self._format_jsonerror(vim_response)
sousaedu80135b92021-02-17 15:05:18 +01001631 text = (
1632 'Error in VIM "{}": not possible to connect external port to network. HTTP Response: {}.'
1633 " Error: {}".format(self.url_admin, vim_response.status_code, jsonerror)
1634 )
tierno7d782ef2019-10-04 12:56:31 +00001635 print(text)
sousaedu80135b92021-02-17 15:05:18 +01001636 return -vim_response.status_code, text