Extracting Ns._create_task() and creating unit test
[osm/RO.git] / RO-VIM-gcp / osm_rovim_gcp / vimconn_gcp.py
1 # -*- coding: utf-8 -*-
2 ##
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14 ##
15
16 import base64
17 from osm_ro_plugin import vimconn
18 import logging
19 import time
20 import random
21 from random import choice as random_choice
22 from os import getenv
23
24 from google.api_core.exceptions import NotFound
25 import googleapiclient.discovery
26 from google.oauth2 import service_account
27
28 from cryptography.hazmat.primitives import serialization as crypto_serialization
29 from cryptography.hazmat.primitives.asymmetric import rsa
30 from cryptography.hazmat.backends import default_backend as crypto_default_backend
31
32 import logging
33
34 __author__ = "Sergio Gallardo Ruiz"
35 __date__ = "$11-aug-2021 08:30:00$"
36
37
38 if getenv("OSMRO_PDB_DEBUG"):
39 import sys
40
41 print(sys.path)
42 import pdb
43
44 pdb.set_trace()
45
46
47 class vimconnector(vimconn.VimConnector):
48
49 # Translate Google Cloud provisioning state to OSM provision state
50 # The first three ones are the transitional status once a user initiated action has been requested
51 # Once the operation is complete, it will transition into the states Succeeded or Failed
52 # https://cloud.google.com/compute/docs/instances/instance-life-cycle
53 provision_state2osm = {
54 "PROVISIONING": "BUILD",
55 "REPAIRING": "ERROR",
56 }
57
58 # Translate azure power state to OSM provision state
59 power_state2osm = {
60 "STAGING": "BUILD",
61 "RUNNING": "ACTIVE",
62 "STOPPING": "INACTIVE",
63 "SUSPENDING": "INACTIVE",
64 "SUSPENDED": "INACTIVE",
65 "TERMINATED": "INACTIVE",
66 }
67
68 # If a net or subnet is tried to be deleted and it has an associated resource, the net is marked "to be deleted"
69 # (incluid it's name in the following list). When the instance is deleted, its associated net will be deleted if
70 # they are present in that list
71 nets_to_be_deleted = []
72
73 def __init__(
74 self,
75 uuid,
76 name,
77 tenant_id,
78 tenant_name,
79 url,
80 url_admin=None,
81 user=None,
82 passwd=None,
83 log_level=None,
84 config={},
85 persistent_info={},
86 ):
87 """
88 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
89 checking against the VIM
90 Using common constructor parameters.
91 In this case: config must include the following parameters:
92 subscription_id: assigned subscription identifier
93 region_name: current region for network
94 config may also include the following parameter:
95 flavors_pattern: pattern that will be used to select a range of vm sizes, for example
96 "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused
97 "^Standard_B" will select a serie B maybe for test environment
98 """
99 vimconn.VimConnector.__init__(
100 self,
101 uuid,
102 name,
103 tenant_id,
104 tenant_name,
105 url,
106 url_admin,
107 user,
108 passwd,
109 log_level,
110 config,
111 persistent_info,
112 )
113
114 # Variable that indicates if client must be reloaded or initialized
115 self.reload_client = False
116
117 # LOGGER
118
119 log_format_simple = (
120 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
121 )
122 log_format_complete = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(funcName)s(): %(message)s"
123 log_formatter_simple = logging.Formatter(
124 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
125 )
126 self.handler = logging.StreamHandler()
127 self.handler.setFormatter(log_formatter_simple)
128
129 self.logger = logging.getLogger("ro.vim.gcp")
130 self.logger.addHandler(self.handler)
131 if log_level:
132 self.logger.setLevel(getattr(logging, log_level))
133
134 if self.logger.getEffectiveLevel() == logging.DEBUG:
135 log_formatter = logging.Formatter(
136 log_format_complete, datefmt="%Y-%m-%dT%H:%M:%S"
137 )
138 self.handler.setFormatter(log_formatter)
139
140 self.logger.debug("Google Cloud connection init")
141
142 self.project = tenant_id or tenant_name
143
144 # REGION - Google Cloud considers regions and zones. A specific region can have more than one zone
145 # (for instance: region us-west1 with the zones us-west1-a, us-west1-b and us-west1-c)
146 # So the region name specified in the config will be considered as a specific zone for GC and
147 # the region will be calculated from that without the preffix.
148 if "region_name" in config:
149 self.zone = config.get("region_name")
150 self.region = self.zone.rsplit("-", 1)[0]
151 else:
152 raise vimconn.VimConnException(
153 "Google Cloud region_name is not specified at config"
154 )
155
156 # Credentials
157 self.logger.debug("Config: %s", config)
158 scopes = ["https://www.googleapis.com/auth/cloud-platform"]
159 self.credentials = None
160 if "credentials" in config:
161 self.logger.debug("Setting credentials")
162 # Settings Google Cloud credentials dict
163 credentials_body = config["credentials"]
164 # self.logger.debug("Credentials filtered: %s", credentials_body)
165 credentials = service_account.Credentials.from_service_account_info(
166 credentials_body
167 )
168 if "sa_file" in config:
169 credentials = service_account.Credentials.from_service_account_file(
170 config.get("sa_file"), scopes=scopes
171 )
172 self.logger.debug("Credentials: %s", credentials)
173 # Construct a Resource for interacting with an API.
174 self.credentials = credentials
175 try:
176 self.conn_compute = googleapiclient.discovery.build(
177 "compute", "v1", credentials=credentials
178 )
179 except Exception as e:
180 self._format_vimconn_exception(e)
181 else:
182 raise vimconn.VimConnException(
183 "It is not possible to init GCP with no credentials"
184 )
185
186 def _reload_connection(self):
187 """
188 Called before any operation, checks python Google Cloud clientsself.reload_client
189 """
190 if self.reload_client:
191 self.logger.debug("reloading google cloud client")
192
193 try:
194 # Set to client created
195 self.conn_compute = googleapiclient.discovery.build("compute", "v1")
196 except Exception as e:
197 self._format_vimconn_exception(e)
198
199 def _format_vimconn_exception(self, e):
200 """
201 Transforms a generic exception to a vimConnException
202 """
203 self.logger.error("Google Cloud plugin error: {}".format(e))
204 if isinstance(e, vimconn.VimConnException):
205 raise e
206 else:
207 # In case of generic error recreate client
208 self.reload_client = True
209 raise vimconn.VimConnException(type(e).__name__ + ": " + str(e))
210
211 def _wait_for_global_operation(self, operation):
212 """
213 Waits for the end of the specific operation
214 :operation: operation name
215 """
216
217 self.logger.debug("Waiting for operation %s", operation)
218
219 while True:
220 result = (
221 self.conn_compute.globalOperations()
222 .get(project=self.project, operation=operation)
223 .execute()
224 )
225
226 if result["status"] == "DONE":
227 if "error" in result:
228 raise vimconn.VimConnException(result["error"])
229 return result
230
231 time.sleep(1)
232
233 def _wait_for_zone_operation(self, operation):
234 """
235 Waits for the end of the specific operation
236 :operation: operation name
237 """
238
239 self.logger.debug("Waiting for operation %s", operation)
240
241 while True:
242 result = (
243 self.conn_compute.zoneOperations()
244 .get(project=self.project, operation=operation, zone=self.zone)
245 .execute()
246 )
247
248 if result["status"] == "DONE":
249 if "error" in result:
250 raise vimconn.VimConnException(result["error"])
251 return result
252
253 time.sleep(1)
254
255 def _wait_for_region_operation(self, operation):
256 """
257 Waits for the end of the specific operation
258 :operation: operation name
259 """
260
261 self.logger.debug("Waiting for operation %s", operation)
262
263 while True:
264 result = (
265 self.conn_compute.regionOperations()
266 .get(project=self.project, operation=operation, region=self.region)
267 .execute()
268 )
269
270 if result["status"] == "DONE":
271 if "error" in result:
272 raise vimconn.VimConnException(result["error"])
273 return result
274
275 time.sleep(1)
276
277 def new_network(
278 self,
279 net_name,
280 net_type,
281 ip_profile=None,
282 shared=False,
283 provider_network_profile=None,
284 ):
285 """
286 Adds a network to VIM
287 :param net_name: name of the network
288 :param net_type: not used for Google Cloud networks
289 :param ip_profile: not used for Google Cloud networks
290 :param shared: Not allowed for Google Cloud Connector
291 :param provider_network_profile: (optional)
292
293 contains {segmentation-id: vlan, provider-network: vim_netowrk}
294 :return: a tuple with the network identifier and created_items, or raises an exception on error
295 created_items can be None or a dictionary where this method can include key-values that will be passed to
296 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
297 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
298 as not present.
299 """
300
301 self.logger.debug(
302 "new_network begin: net_name %s net_type %s ip_profile %s shared %s provider_network_profile %s",
303 net_name,
304 net_type,
305 ip_profile,
306 shared,
307 provider_network_profile,
308 )
309 net_name = self._check_vm_name(net_name)
310 net_name = self._randomize_name(net_name)
311 self.logger.debug("create network name %s, ip_profile %s", net_name, ip_profile)
312
313 try:
314
315 self.logger.debug("creating network_name: {}".format(net_name))
316
317 network = "projects/{}/global/networks/default".format(self.project)
318 subnet_address = ""
319 if ip_profile is not None:
320 if "subnet_address" in ip_profile:
321 subnet_address = ip_profile["subnet_address"]
322 network_body = {
323 "name": str(net_name),
324 "description": net_name,
325 "network": network,
326 "ipCidrRange": subnet_address,
327 # "autoCreateSubnetworks": True, # The network is created in AUTO mode (one subnet per region is created)
328 "autoCreateSubnetworks": False,
329 }
330
331 operation = (
332 self.conn_compute.networks()
333 .insert(project=self.project, body=network_body)
334 .execute()
335 )
336 self._wait_for_global_operation(operation["name"])
337 self.logger.debug("created network_name: {}".format(net_name))
338
339 # Adding firewall rules to allow the traffic in the network:
340 rules_list = self._create_firewall_rules(net_name)
341
342 # create subnetwork, even if there is no profile
343
344 if not ip_profile:
345 ip_profile = {}
346
347 if not ip_profile.get("subnet_address"):
348 # Fake subnet is required
349 subnet_rand = random.randint(0, 255)
350 ip_profile["subnet_address"] = "192.168.{}.0/24".format(subnet_rand)
351
352 subnet_name = net_name + "-subnet"
353 subnet_id = self._new_subnet(
354 subnet_name, ip_profile, operation["targetLink"]
355 )
356
357 self.logger.debug("new_network Return: subnet_id: %s", subnet_id)
358 return subnet_id
359 except Exception as e:
360 self._format_vimconn_exception(e)
361
362 def _new_subnet(self, subnet_name, ip_profile, network):
363 """
364 Adds a tenant network to VIM. It creates a new subnet at existing base vnet
365 :param net_name: subnet name
366 :param ip_profile:
367 subnet-address: if it is not provided a subnet/24 in the default vnet is created,
368 otherwise it creates a subnet in the indicated address
369 :return: a tuple with the network identifier and created_items, or raises an exception on error
370 """
371 self.logger.debug(
372 "_new_subnet begin: subnet_name %s ip_profile %s network %s",
373 subnet_name,
374 ip_profile,
375 network,
376 )
377 self.logger.debug(
378 "create subnet name %s, ip_profile %s", subnet_name, ip_profile
379 )
380
381 try:
382
383 self.logger.debug("creating subnet_name: {}".format(subnet_name))
384
385 subnetwork_body = {
386 "name": str(subnet_name),
387 "description": subnet_name,
388 "network": network,
389 "ipCidrRange": ip_profile["subnet_address"],
390 }
391
392 operation = (
393 self.conn_compute.subnetworks()
394 .insert(
395 project=self.project,
396 region=self.region,
397 body=subnetwork_body,
398 )
399 .execute()
400 )
401 self._wait_for_region_operation(operation["name"])
402
403 self.logger.debug("created subnet_name: {}".format(subnet_name))
404
405 self.logger.debug(
406 "_new_subnet Return: (%s,%s)",
407 "regions/%s/subnetworks/%s" % (self.region, subnet_name),
408 None,
409 )
410 return "regions/%s/subnetworks/%s" % (self.region, subnet_name), None
411 except Exception as e:
412 self._format_vimconn_exception(e)
413
414 def get_network_list(self, filter_dict={}):
415 """Obtain tenant networks of VIM
416 Filter_dict can be:
417 name: network name
418 id: network id
419 shared: boolean, not implemented in GC
420 tenant_id: tenant, not used in GC, all networks same tenants
421 admin_state_up: boolean, not implemented in GC
422 status: 'ACTIVE', not implemented in GC #
423 Returns the network list of dictionaries
424 """
425 self.logger.debug("get_network_list begin: filter_dict %s", filter_dict)
426 self.logger.debug(
427 "Getting network (subnetwork) from VIM filter: {}".format(str(filter_dict))
428 )
429
430 try:
431
432 if self.reload_client:
433 self._reload_connection()
434
435 net_list = []
436
437 request = self.conn_compute.subnetworks().list(
438 project=self.project, region=self.region
439 )
440
441 while request is not None:
442 response = request.execute()
443 self.logger.debug("Network list: %s", response)
444 for net in response["items"]:
445 self.logger.debug("Network in list: {}".format(str(net["name"])))
446 if filter_dict is not None:
447 if "name" in filter_dict.keys():
448 if (
449 filter_dict["name"] == net["name"]
450 or filter_dict["name"] == net["selfLink"]
451 ):
452 self.logger.debug("Network found: %s", net["name"])
453 net_list.append(
454 {
455 "id": str(net["selfLink"]),
456 "name": str(net["name"]),
457 "network": str(net["network"]),
458 }
459 )
460 else:
461 net_list.append(
462 {
463 "id": str(net["selfLink"]),
464 "name": str(net["name"]),
465 "network": str(net["network"]),
466 }
467 )
468 request = self.conn_compute.subnetworks().list_next(
469 previous_request=request, previous_response=response
470 )
471
472 self.logger.debug("get_network_list Return: net_list %s", net_list)
473 return net_list
474
475 except Exception as e:
476 self.logger.error("Error in get_network_list()", exc_info=True)
477 raise vimconn.VimConnException(e)
478
479 def get_network(self, net_id):
480 self.logger.debug("get_network begin: net_id %s", net_id)
481 # res_name = self._get_resource_name_from_resource_id(net_id)
482 self._reload_connection()
483
484 self.logger.debug("Get network: %s", net_id)
485 filter_dict = {"name": net_id}
486 network_list = self.get_network_list(filter_dict)
487 self.logger.debug("Network list: %s", network_list)
488
489 if not network_list:
490 return []
491 else:
492 self.logger.debug("get_network Return: network_list[0] %s", network_list[0])
493 return network_list[0]
494
495 def delete_network(self, net_id, created_items=None):
496 """
497 Removes a tenant network from VIM and its associated elements
498 :param net_id: VIM identifier of the network, provided by method new_network
499 :param created_items: dictionary with extra items to be deleted. provided by method new_network
500 Returns the network identifier or raises an exception upon error or when network is not found
501 """
502
503 self.logger.debug(
504 "delete_network begin: net_id %s created_items %s",
505 net_id,
506 created_items,
507 )
508 self.logger.debug("Deleting network: {}".format(str(net_id)))
509
510 try:
511
512 net_name = self._get_resource_name_from_resource_id(net_id)
513
514 # Check associated VMs
515 vms = (
516 self.conn_compute.instances()
517 .list(project=self.project, zone=self.zone)
518 .execute()
519 )
520
521 net_id = self.delete_subnet(net_name, created_items)
522
523 self.logger.debug("delete_network Return: net_id %s", net_id)
524 return net_id
525
526 except Exception as e:
527 self.logger.error("Error in delete_network()", exc_info=True)
528 raise vimconn.VimConnException(e)
529
530 def delete_subnet(self, net_id, created_items=None):
531 """
532 Removes a tenant network from VIM and its associated elements
533 :param net_id: VIM identifier of the network, provided by method new_network
534 :param created_items: dictionary with extra items to be deleted. provided by method new_network
535 Returns the network identifier or raises an exception upon error or when network is not found
536 """
537
538 self.logger.debug(
539 "delete_subnet begin: net_id %s created_items %s",
540 net_id,
541 created_items,
542 )
543 self.logger.debug("Deleting subnetwork: {}".format(str(net_id)))
544
545 try:
546 # If the network has no more subnets, it will be deleted too
547 net_info = self.get_network(net_id)
548 # If the subnet is in use by another resource, the deletion will be retried N times before abort the operation
549 created_items = created_items or {}
550 created_items[net_id] = False
551
552 try:
553 operation = (
554 self.conn_compute.subnetworks()
555 .delete(
556 project=self.project,
557 region=self.region,
558 subnetwork=net_id,
559 )
560 .execute()
561 )
562 self._wait_for_region_operation(operation["name"])
563 if net_id in self.nets_to_be_deleted:
564 self.nets_to_be_deleted.remove(net_id)
565 except Exception as e:
566 if (
567 e.args[0]["status"] == "400"
568 ): # Resource in use, so the net is marked to be deleted
569 self.logger.debug("Subnet still in use")
570 self.nets_to_be_deleted.append(net_id)
571 else:
572 raise vimconn.VimConnException(e)
573
574 self.logger.debug("nets_to_be_deleted: %s", self.nets_to_be_deleted)
575
576 # If the network has no more subnets, it will be deleted too
577 # if "network" in net_info and net_id not in self.nets_to_be_deleted:
578 if "network" in net_info:
579 network_name = self._get_resource_name_from_resource_id(
580 net_info["network"]
581 )
582
583 try:
584 # Deletion of the associated firewall rules:
585 rules_list = self._delete_firewall_rules(network_name)
586
587 operation = (
588 self.conn_compute.networks()
589 .delete(
590 project=self.project,
591 network=network_name,
592 )
593 .execute()
594 )
595 self._wait_for_global_operation(operation["name"])
596 except Exception as e:
597 self.logger.debug("error deleting associated network %s", e)
598
599 self.logger.debug("delete_subnet Return: net_id %s", net_id)
600 return net_id
601
602 except Exception as e:
603 self.logger.error("Error in delete_network()", exc_info=True)
604 raise vimconn.VimConnException(e)
605
606 def new_flavor(self, flavor_data):
607 """
608 It is not allowed to create new flavors (machine types) in Google Cloud, must always use an existing one
609 """
610 raise vimconn.VimConnNotImplemented(
611 "It is not possible to create new flavors in Google Cloud"
612 )
613
614 def new_tenant(self, tenant_name, tenant_description):
615 """
616 It is not allowed to create new tenants in Google Cloud
617 """
618 raise vimconn.VimConnNotImplemented(
619 "It is not possible to create a TENANT in Google Cloud"
620 )
621
622 def get_flavor(self, flavor_id):
623 """
624 Obtains the flavor_data from the flavor_id/machine type id
625 """
626 self.logger.debug("get_flavor begin: flavor_id %s", flavor_id)
627
628 try:
629 response = (
630 self.conn_compute.machineTypes()
631 .get(project=self.project, zone=self.zone, machineType=flavor_id)
632 .execute()
633 )
634 flavor_data = response
635 self.logger.debug("Machine type data: %s", flavor_data)
636
637 if flavor_data:
638 flavor = {
639 "id": flavor_data["id"],
640 "name": flavor_id,
641 "id_complete": flavor_data["selfLink"],
642 "ram": flavor_data["memoryMb"],
643 "vcpus": flavor_data["guestCpus"],
644 "disk": flavor_data["maximumPersistentDisksSizeGb"],
645 }
646
647 self.logger.debug("get_flavor Return: flavor %s", flavor)
648 return flavor
649 else:
650 raise vimconn.VimConnNotFoundException(
651 "flavor '{}' not found".format(flavor_id)
652 )
653 except Exception as e:
654 self._format_vimconn_exception(e)
655
656 # Google Cloud VM names can not have some special characters
657 def _check_vm_name(self, vm_name):
658 """
659 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
660 Only lowercase and hyphens are allowed
661 """
662 chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?."
663
664 # First: the VM name max length is 64 characters
665 vm_name_aux = vm_name[:62]
666
667 # Second: replace not allowed characters
668 for elem in chars_not_allowed_list:
669 # Check if string is in the main string
670 if elem in vm_name_aux:
671 # self.logger.debug("Dentro del IF")
672 # Replace the string
673 vm_name_aux = vm_name_aux.replace(elem, "-")
674
675 return vm_name_aux.lower()
676
677 def get_flavor_id_from_data(self, flavor_dict):
678 self.logger.debug("get_flavor_id_from_data begin: flavor_dict %s", flavor_dict)
679 filter_dict = flavor_dict or {}
680
681 try:
682 response = (
683 self.conn_compute.machineTypes()
684 .list(project=self.project, zone=self.zone)
685 .execute()
686 )
687 machine_types_list = response["items"]
688 # self.logger.debug("List of machine types: %s", machine_types_list)
689
690 cpus = filter_dict.get("vcpus") or 0
691 memMB = filter_dict.get("ram") or 0
692 # Workaround (it should be 0)
693 numberInterfaces = len(filter_dict.get("interfaces", [])) or 4
694
695 # Filter
696 filtered_machines = []
697 for machine_type in machine_types_list:
698 if (
699 machine_type["guestCpus"] >= cpus
700 and machine_type["memoryMb"] >= memMB
701 # In Google Cloud the number of virtual network interfaces scales with
702 # the number of virtual CPUs with a minimum of 2 and a maximum of 8:
703 # https://cloud.google.com/vpc/docs/create-use-multiple-interfaces#max-interfaces
704 and machine_type["guestCpus"] >= numberInterfaces
705 ):
706 filtered_machines.append(machine_type)
707
708 # self.logger.debug("Filtered machines: %s", filtered_machines)
709
710 # Sort
711 listedFilteredMachines = sorted(
712 filtered_machines,
713 key=lambda k: (
714 int(k["guestCpus"]),
715 float(k["memoryMb"]),
716 int(k["maximumPersistentDisksSizeGb"]),
717 k["name"],
718 ),
719 )
720 # self.logger.debug("Sorted filtered machines: %s", listedFilteredMachines)
721
722 if listedFilteredMachines:
723 self.logger.debug(
724 "get_flavor_id_from_data Return: listedFilteredMachines[0][name] %s",
725 listedFilteredMachines[0]["name"],
726 )
727 return listedFilteredMachines[0]["name"]
728
729 raise vimconn.VimConnNotFoundException(
730 "Cannot find any flavor matching '{}'".format(str(flavor_dict))
731 )
732
733 except Exception as e:
734 self._format_vimconn_exception(e)
735
736 def delete_flavor(self, flavor_id):
737 raise vimconn.VimConnNotImplemented(
738 "It is not possible to delete a flavor in Google Cloud"
739 )
740
741 def delete_tenant(self, tenant_id):
742 raise vimconn.VimConnNotImplemented(
743 "It is not possible to delete a TENANT in Google Cloud"
744 )
745
746 def new_image(self, image_dict):
747 """
748 This function comes from the early days when we though the image could be embedded in the package.
749 Unless OSM manages VM images E2E from NBI to RO, this function does not make sense to be implemented.
750 """
751 raise vimconn.VimConnNotImplemented("Not implemented")
752
753 def get_image_id_from_path(self, path):
754 """
755 This function comes from the early days when we though the image could be embedded in the package.
756 Unless OSM manages VM images E2E from NBI to RO, this function does not make sense to be implemented.
757 """
758 raise vimconn.VimConnNotImplemented("Not implemented")
759
760 def get_image_list(self, filter_dict={}):
761 """Obtain tenant images from VIM
762 Filter_dict can be:
763 name: image name with the format: image project:image family:image version
764 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
765 for the provided publisher and offer
766 id: image uuid, currently not supported for azure
767 Returns the image list of dictionaries:
768 [{<the fields at Filter_dict plus some VIM specific>}, ...]
769 List can be empty
770 """
771 self.logger.debug("get_image_list begin: filter_dict %s", filter_dict)
772
773 try:
774 image_list = []
775 # Get image id from parameter image_id:
776 # <image Project>:image-family:<family> => Latest version of the family
777 # <image Project>:image:<image> => Specific image
778 # <image Project>:<image> => Specific image
779
780 image_info = filter_dict["name"].split(":")
781 image_project = image_info[0]
782 if len(image_info) == 2:
783 image_type = "image"
784 image_item = image_info[1]
785 if len(image_info) == 3:
786 image_type = image_info[1]
787 image_item = image_info[2]
788 else:
789 raise vimconn.VimConnNotFoundException("Wrong format for image")
790
791 image_response = {}
792 if image_type == "image-family":
793 image_response = (
794 self.conn_compute.images()
795 .getFromFamily(project=image_project, family=image_item)
796 .execute()
797 )
798 elif image_type == "image":
799 image_response = (
800 self.conn_compute.images()
801 .get(project=image_project, image=image_item)
802 .execute()
803 )
804 else:
805 raise vimconn.VimConnNotFoundException("Wrong format for image")
806 image_list.append(
807 {
808 "id": "projects/%s/global/images/%s"
809 % (image_project, image_response["name"]),
810 "name": ":".join(
811 [image_project, image_item, image_response["name"]]
812 ),
813 }
814 )
815
816 self.logger.debug("get_image_list Return: image_list %s", image_list)
817 return image_list
818
819 except Exception as e:
820 self._format_vimconn_exception(e)
821
822 def delete_inuse_nic(self, nic_name):
823 raise vimconn.VimConnNotImplemented("Not necessary")
824
825 def delete_image(self, image_id):
826 raise vimconn.VimConnNotImplemented("Not implemented")
827
828 def action_vminstance(self, vm_id, action_dict, created_items={}):
829 """Send and action over a VM instance from VIM
830 Returns the vm_id if the action was successfully sent to the VIM
831 """
832 raise vimconn.VimConnNotImplemented("Not necessary")
833
834 def _randomize_name(self, name):
835 """Adds a random string to allow requests with the same VM name
836 Returns the name with an additional random string (if the total size is bigger
837 than 62 the original name will be truncated)
838 """
839 random_name = name
840
841 while True:
842 try:
843 random_name = (
844 name[:49]
845 + "-"
846 + "".join(random_choice("0123456789abcdef") for _ in range(12))
847 )
848 response = (
849 self.conn_compute.instances()
850 .get(project=self.project, zone=self.zone, instance=random_name)
851 .execute()
852 )
853 # If no exception is arisen, the random name exists for an instance, so a new random name must be generated
854
855 except Exception as e:
856 if e.args[0]["status"] == "404":
857 self.logger.debug("New random name: %s", random_name)
858 break
859 else:
860 self.logger.error(
861 "Exception generating random name (%s) for the instance", name
862 )
863 self._format_vimconn_exception(e)
864
865 return random_name
866
867 def new_vminstance(
868 self,
869 name,
870 description,
871 start,
872 image_id=None, # <image project>:(image|image-family):<image/family id>
873 flavor_id=None,
874 net_list=None,
875 cloud_config=None,
876 disk_list=None,
877 availability_zone_index=None,
878 availability_zone_list=None,
879 ):
880 self.logger.debug(
881 "new_vminstance begin: name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, "
882 "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
883 name,
884 image_id,
885 flavor_id,
886 net_list,
887 cloud_config,
888 disk_list,
889 availability_zone_index,
890 availability_zone_list,
891 )
892
893 if self.reload_client:
894 self._reload_connection()
895
896 # Validate input data is valid
897 # # First of all, the name must be adapted because Google Cloud only allows names consist of
898 # lowercase letters (a-z), numbers and hyphens (?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)
899 vm_name = self._check_vm_name(name)
900 vm_name = self._randomize_name(vm_name)
901 vm_id = None
902
903 # At least one network must be provided
904 if not net_list:
905 raise vimconn.VimConnException(
906 "At least one net must be provided to create a new VM"
907 )
908
909 try:
910 created_items = {}
911 metadata = self._build_metadata(vm_name, cloud_config)
912
913 # Building network interfaces list
914 network_interfaces = []
915 for net in net_list:
916 net_iface = {}
917 if not net.get("net_id"):
918 if not net.get("name"):
919 continue
920 else:
921 net_iface[
922 "subnetwork"
923 ] = "regions/%s/subnetworks/" % self.region + net.get("name")
924 else:
925 net_iface["subnetwork"] = net.get("net_id")
926 # In order to get an external IP address, the key "accessConfigs" must be used
927 # in the interace. It has to be of type "ONE_TO_ONE_NAT" and name "External NAT"
928 if net.get("floating_ip", False) or (
929 net["use"] == "mgmt" and self.config.get("use_floating_ip")
930 ):
931 net_iface["accessConfigs"] = [
932 {"type": "ONE_TO_ONE_NAT", "name": "External NAT"}
933 ]
934
935 network_interfaces.append(net_iface)
936
937 self.logger.debug("Network interfaces: %s", network_interfaces)
938
939 self.logger.debug("Source image: %s", image_id)
940
941 vm_parameters = {
942 "name": vm_name,
943 "machineType": self.get_flavor(flavor_id)["id_complete"],
944 # Specify the boot disk and the image to use as a source.
945 "disks": [
946 {
947 "boot": True,
948 "autoDelete": True,
949 "initializeParams": {
950 "sourceImage": image_id,
951 },
952 }
953 ],
954 # Specify the network interfaces
955 "networkInterfaces": network_interfaces,
956 "metadata": metadata,
957 }
958
959 response = (
960 self.conn_compute.instances()
961 .insert(project=self.project, zone=self.zone, body=vm_parameters)
962 .execute()
963 )
964 self._wait_for_zone_operation(response["name"])
965
966 # The created instance info is obtained to get the name of the generated network interfaces (nic0, nic1...)
967 response = (
968 self.conn_compute.instances()
969 .get(project=self.project, zone=self.zone, instance=vm_name)
970 .execute()
971 )
972 self.logger.debug("instance get: %s", response)
973 vm_id = response["name"]
974
975 # The generated network interfaces in the instance are include in net_list:
976 for _, net in enumerate(net_list):
977 for net_ifaces in response["networkInterfaces"]:
978 network_id = ""
979 if "net_id" in net:
980 network_id = self._get_resource_name_from_resource_id(
981 net["net_id"]
982 )
983 else:
984 network_id = self._get_resource_name_from_resource_id(
985 net["name"]
986 )
987 if network_id == self._get_resource_name_from_resource_id(
988 net_ifaces["subnetwork"]
989 ):
990 net["vim_id"] = net_ifaces["name"]
991
992 self.logger.debug(
993 "new_vminstance Return: (name %s, created_items %s)",
994 vm_name,
995 created_items,
996 )
997 return vm_name, created_items
998
999 except Exception as e:
1000 # Rollback vm creacion
1001 if vm_id is not None:
1002 try:
1003 self.logger.debug("exception creating vm try to rollback")
1004 self.delete_vminstance(vm_id, created_items)
1005 except Exception as e2:
1006 self.logger.error("new_vminstance rollback fail {}".format(e2))
1007
1008 else:
1009 self.logger.debug(
1010 "Exception creating new vminstance: %s", e, exc_info=True
1011 )
1012 self._format_vimconn_exception(e)
1013
1014 def _build_metadata(self, vm_name, cloud_config):
1015
1016 # initial metadata
1017 metadata = {}
1018 metadata["items"] = []
1019 key_pairs = {}
1020
1021 # if there is a cloud-init load it
1022 if cloud_config:
1023 self.logger.debug("cloud config: %s", cloud_config)
1024 _, userdata = self._create_user_data(cloud_config)
1025 metadata["items"].append({"key": "user-data", "value": userdata})
1026
1027 # either password of ssh-keys are required
1028 # we will always use ssh-keys, in case it is not available we will generate it
1029 """
1030 if cloud_config and cloud_config.get("key-pairs"):
1031 key_data = ""
1032 key_pairs = {}
1033 if cloud_config.get("key-pairs"):
1034 if isinstance(cloud_config["key-pairs"], list):
1035 # Transform the format "<key> <user@host>" into "<user>:<key>"
1036 key_data = ""
1037 for key in cloud_config.get("key-pairs"):
1038 key_data = key_data + key + "\n"
1039 key_pairs = {
1040 "key": "ssh-keys",
1041 "value": key_data
1042 }
1043 else:
1044 # If there is no ssh key in cloud config, a new key is generated:
1045 _, key_data = self._generate_keys()
1046 key_pairs = {
1047 "key": "ssh-keys",
1048 "value": "" + key_data
1049 }
1050 self.logger.debug("generated keys: %s", key_data)
1051
1052 metadata["items"].append(key_pairs)
1053 """
1054 self.logger.debug("metadata: %s", metadata)
1055
1056 return metadata
1057
1058 def _generate_keys(self):
1059 """Method used to generate a pair of private/public keys.
1060 This method is used because to create a vm in Azure we always need a key or a password
1061 In some cases we may have a password in a cloud-init file but it may not be available
1062 """
1063 key = rsa.generate_private_key(
1064 backend=crypto_default_backend(), public_exponent=65537, key_size=2048
1065 )
1066 private_key = key.private_bytes(
1067 crypto_serialization.Encoding.PEM,
1068 crypto_serialization.PrivateFormat.PKCS8,
1069 crypto_serialization.NoEncryption(),
1070 )
1071 public_key = key.public_key().public_bytes(
1072 crypto_serialization.Encoding.OpenSSH,
1073 crypto_serialization.PublicFormat.OpenSSH,
1074 )
1075 private_key = private_key.decode("utf8")
1076 # Change first line because Paramiko needs a explicit start with 'BEGIN RSA PRIVATE KEY'
1077 i = private_key.find("\n")
1078 private_key = "-----BEGIN RSA PRIVATE KEY-----" + private_key[i:]
1079 public_key = public_key.decode("utf8")
1080
1081 return private_key, public_key
1082
1083 def _get_unused_vm_name(self, vm_name):
1084 """
1085 Checks the vm name and in case it is used adds a suffix to the name to allow creation
1086 :return:
1087 """
1088 all_vms = (
1089 self.conn_compute.instances()
1090 .list(project=self.project, zone=self.zone)
1091 .execute()
1092 )
1093 # Filter to vms starting with the indicated name
1094 vms = list(filter(lambda vm: (vm.name.startswith(vm_name)), all_vms))
1095 vm_names = [str(vm.name) for vm in vms]
1096
1097 # get the name with the first not used suffix
1098 name_suffix = 0
1099 # name = subnet_name + "-" + str(name_suffix)
1100 name = vm_name # first subnet created will have no prefix
1101
1102 while name in vm_names:
1103 name_suffix += 1
1104 name = vm_name + "-" + str(name_suffix)
1105
1106 return name
1107
1108 def get_vminstance(self, vm_id):
1109 """
1110 Obtaing the vm instance data from v_id
1111 """
1112 self.logger.debug("get_vminstance begin: vm_id %s", vm_id)
1113 self._reload_connection()
1114 response = {}
1115 try:
1116 response = (
1117 self.conn_compute.instances()
1118 .get(project=self.project, zone=self.zone, instance=vm_id)
1119 .execute()
1120 )
1121 # vm = response["source"]
1122 except Exception as e:
1123 self._format_vimconn_exception(e)
1124
1125 self.logger.debug("get_vminstance Return: response %s", response)
1126 return response
1127
1128 def delete_vminstance(self, vm_id, created_items=None):
1129 """Deletes a vm instance from the vim."""
1130 self.logger.debug(
1131 "delete_vminstance begin: vm_id %s created_items %s",
1132 vm_id,
1133 created_items,
1134 )
1135 if self.reload_client:
1136 self._reload_connection()
1137
1138 created_items = created_items or {}
1139 try:
1140 vm = self.get_vminstance(vm_id)
1141
1142 operation = (
1143 self.conn_compute.instances()
1144 .delete(project=self.project, zone=self.zone, instance=vm_id)
1145 .execute()
1146 )
1147 self._wait_for_zone_operation(operation["name"])
1148
1149 # The associated subnets must be checked if they are marked to be deleted
1150 for netIface in vm["networkInterfaces"]:
1151 if (
1152 self._get_resource_name_from_resource_id(netIface["subnetwork"])
1153 in self.nets_to_be_deleted
1154 ):
1155 net_id = self._get_resource_name_from_resource_id(
1156 self.delete_network(netIface["subnetwork"])
1157 )
1158
1159 self.logger.debug("delete_vminstance end")
1160
1161 except Exception as e:
1162 # The VM can be deleted previously during network deletion
1163 if e.args[0]["status"] == "404":
1164 self.logger.debug("The VM doesn't exist or has been deleted")
1165 else:
1166 self._format_vimconn_exception(e)
1167
1168 def _get_net_name_from_resource_id(self, resource_id):
1169 try:
1170 net_name = str(resource_id.split("/")[-1])
1171
1172 return net_name
1173 except Exception:
1174 raise vimconn.VimConnException(
1175 "Unable to get google cloud net_name from invalid resource_id format '{}'".format(
1176 resource_id
1177 )
1178 )
1179
1180 def _get_resource_name_from_resource_id(self, resource_id):
1181 """
1182 Obtains resource_name from the google cloud complete identifier: resource_name will always be last item
1183 """
1184 self.logger.debug(
1185 "_get_resource_name_from_resource_id begin: resource_id %s",
1186 resource_id,
1187 )
1188 try:
1189 resource = str(resource_id.split("/")[-1])
1190
1191 self.logger.debug(
1192 "_get_resource_name_from_resource_id Return: resource %s",
1193 resource,
1194 )
1195 return resource
1196 except Exception as e:
1197 raise vimconn.VimConnException(
1198 "Unable to get resource name from resource_id '{}' Error: '{}'".format(
1199 resource_id, e
1200 )
1201 )
1202
1203 def refresh_nets_status(self, net_list):
1204 """Get the status of the networks
1205 Params: the list of network identifiers
1206 Returns a dictionary with:
1207 net_id: #VIM id of this network
1208 status: #Mandatory. Text with one of:
1209 # DELETED (not found at vim)
1210 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1211 # OTHER (Vim reported other status not understood)
1212 # ERROR (VIM indicates an ERROR status)
1213 # ACTIVE, INACTIVE, DOWN (admin down),
1214 # BUILD (on building process)
1215 #
1216 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1217 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1218 """
1219 self.logger.debug("refresh_nets_status begin: net_list %s", net_list)
1220 out_nets = {}
1221 self._reload_connection()
1222
1223 for net_id in net_list:
1224 try:
1225 netName = self._get_net_name_from_resource_id(net_id)
1226 resName = self._get_resource_name_from_resource_id(net_id)
1227
1228 net = (
1229 self.conn_compute.subnetworks()
1230 .get(project=self.project, region=self.region, subnetwork=resName)
1231 .execute()
1232 )
1233 self.logger.debug("get subnetwork: %s", net)
1234
1235 out_nets[net_id] = {
1236 "status": "ACTIVE", # Google Cloud does not provide the status in subnetworks getting
1237 "vim_info": str(net),
1238 }
1239 except vimconn.VimConnNotFoundException as e:
1240 self.logger.error(
1241 "VimConnNotFoundException %s when searching subnet", e
1242 )
1243 out_nets[net_id] = {
1244 "status": "DELETED",
1245 "error_msg": str(e),
1246 }
1247 except Exception as e:
1248 self.logger.error(
1249 "Exception %s when searching subnet", e, exc_info=True
1250 )
1251 out_nets[net_id] = {
1252 "status": "VIM_ERROR",
1253 "error_msg": str(e),
1254 }
1255
1256 self.logger.debug("refresh_nets_status Return: out_nets %s", out_nets)
1257 return out_nets
1258
1259 def refresh_vms_status(self, vm_list):
1260 """Get the status of the virtual machines and their interfaces/ports
1261 Params: the list of VM identifiers
1262 Returns a dictionary with:
1263 vm_id: # VIM id of this Virtual Machine
1264 status: # Mandatory. Text with one of:
1265 # DELETED (not found at vim)
1266 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1267 # OTHER (Vim reported other status not understood)
1268 # ERROR (VIM indicates an ERROR status)
1269 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1270 # BUILD (on building process), ERROR
1271 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1272 # (ACTIVE:NoMgmtIP is not returned for Azure)
1273 #
1274 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1275 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1276 interfaces: list with interface info. Each item a dictionary with:
1277 vim_interface_id - The ID of the interface
1278 mac_address - The MAC address of the interface.
1279 ip_address - The IP address of the interface within the subnet.
1280 """
1281 self.logger.debug("refresh_vms_status begin: vm_list %s", vm_list)
1282 out_vms = {}
1283 self._reload_connection()
1284
1285 search_vm_list = vm_list or {}
1286
1287 for vm_id in search_vm_list:
1288 out_vm = {}
1289 try:
1290 res_name = self._get_resource_name_from_resource_id(vm_id)
1291
1292 vm = (
1293 self.conn_compute.instances()
1294 .get(project=self.project, zone=self.zone, instance=res_name)
1295 .execute()
1296 )
1297
1298 out_vm["vim_info"] = str(vm["name"])
1299 out_vm["status"] = self.provision_state2osm.get(vm["status"], "OTHER")
1300
1301 # In Google Cloud the there is no difference between provision or power status,
1302 # so if provision status method doesn't return a specific state (OTHER), the
1303 # power method is called
1304 if out_vm["status"] == "OTHER":
1305 out_vm["status"] = self.power_state2osm.get(vm["status"], "OTHER")
1306
1307 network_interfaces = vm["networkInterfaces"]
1308 out_vm["interfaces"] = self._get_vm_interfaces_status(
1309 vm_id, network_interfaces
1310 )
1311 except Exception as e:
1312 self.logger.error("Exception %s refreshing vm_status", e, exc_info=True)
1313 out_vm["status"] = "VIM_ERROR"
1314 out_vm["error_msg"] = str(e)
1315 out_vm["vim_info"] = None
1316
1317 out_vms[vm_id] = out_vm
1318
1319 self.logger.debug("refresh_vms_status Return: out_vms %s", out_vms)
1320 return out_vms
1321
1322 def _get_vm_interfaces_status(self, vm_id, interfaces):
1323 """
1324 Gets the interfaces detail for a vm
1325 :param interfaces: List of interfaces.
1326 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1327 """
1328 self.logger.debug(
1329 "_get_vm_interfaces_status begin: vm_id %s interfaces %s",
1330 vm_id,
1331 interfaces,
1332 )
1333 try:
1334 interface_list = []
1335 for network_interface in interfaces:
1336 interface_dict = {}
1337 nic_name = network_interface["name"]
1338 interface_dict["vim_interface_id"] = network_interface["name"]
1339
1340 ips = []
1341 ips.append(network_interface["networkIP"])
1342 interface_dict["ip_address"] = ";".join(ips)
1343 interface_list.append(interface_dict)
1344
1345 self.logger.debug(
1346 "_get_vm_interfaces_status Return: interface_list %s",
1347 interface_list,
1348 )
1349 return interface_list
1350 except Exception as e:
1351 self.logger.error(
1352 "Exception %s obtaining interface data for vm: %s",
1353 e,
1354 vm_id,
1355 exc_info=True,
1356 )
1357 self._format_vimconn_exception(e)
1358
1359 def _get_default_admin_user(self, image_id):
1360 if "ubuntu" in image_id.lower():
1361 return "ubuntu"
1362 else:
1363 return self._default_admin_user
1364
1365 def _create_firewall_rules(self, network):
1366 """
1367 Creates the necessary firewall rules to allow the traffic in the network
1368 (https://cloud.google.com/vpc/docs/firewalls)
1369 :param network.
1370 :return: a list with the names of the firewall rules
1371 """
1372 self.logger.debug("_create_firewall_rules begin: network %s", network)
1373 try:
1374 rules_list = []
1375
1376 # Adding firewall rule to allow http:
1377 self.logger.debug("creating firewall rule to allow http")
1378 firewall_rule_body = {
1379 "name": "fw-rule-http-" + network,
1380 "network": "global/networks/" + network,
1381 "allowed": [{"IPProtocol": "tcp", "ports": ["80"]}],
1382 }
1383 operation_firewall = (
1384 self.conn_compute.firewalls()
1385 .insert(project=self.project, body=firewall_rule_body)
1386 .execute()
1387 )
1388
1389 # Adding firewall rule to allow ssh:
1390 self.logger.debug("creating firewall rule to allow ssh")
1391 firewall_rule_body = {
1392 "name": "fw-rule-ssh-" + network,
1393 "network": "global/networks/" + network,
1394 "allowed": [{"IPProtocol": "tcp", "ports": ["22"]}],
1395 }
1396 operation_firewall = (
1397 self.conn_compute.firewalls()
1398 .insert(project=self.project, body=firewall_rule_body)
1399 .execute()
1400 )
1401
1402 # Adding firewall rule to allow ping:
1403 self.logger.debug("creating firewall rule to allow ping")
1404 firewall_rule_body = {
1405 "name": "fw-rule-icmp-" + network,
1406 "network": "global/networks/" + network,
1407 "allowed": [{"IPProtocol": "icmp"}],
1408 }
1409 operation_firewall = (
1410 self.conn_compute.firewalls()
1411 .insert(project=self.project, body=firewall_rule_body)
1412 .execute()
1413 )
1414
1415 # Adding firewall rule to allow internal:
1416 self.logger.debug("creating firewall rule to allow internal")
1417 firewall_rule_body = {
1418 "name": "fw-rule-internal-" + network,
1419 "network": "global/networks/" + network,
1420 "allowed": [
1421 {"IPProtocol": "tcp", "ports": ["0-65535"]},
1422 {"IPProtocol": "udp", "ports": ["0-65535"]},
1423 {"IPProtocol": "icmp"},
1424 ],
1425 }
1426 operation_firewall = (
1427 self.conn_compute.firewalls()
1428 .insert(project=self.project, body=firewall_rule_body)
1429 .execute()
1430 )
1431
1432 # Adding firewall rule to allow microk8s:
1433 self.logger.debug("creating firewall rule to allow microk8s")
1434 firewall_rule_body = {
1435 "name": "fw-rule-microk8s-" + network,
1436 "network": "global/networks/" + network,
1437 "allowed": [{"IPProtocol": "tcp", "ports": ["16443"]}],
1438 }
1439 operation_firewall = (
1440 self.conn_compute.firewalls()
1441 .insert(project=self.project, body=firewall_rule_body)
1442 .execute()
1443 )
1444
1445 # Adding firewall rule to allow rdp:
1446 self.logger.debug("creating firewall rule to allow rdp")
1447 firewall_rule_body = {
1448 "name": "fw-rule-rdp-" + network,
1449 "network": "global/networks/" + network,
1450 "allowed": [{"IPProtocol": "tcp", "ports": ["3389"]}],
1451 }
1452 operation_firewall = (
1453 self.conn_compute.firewalls()
1454 .insert(project=self.project, body=firewall_rule_body)
1455 .execute()
1456 )
1457
1458 # Adding firewall rule to allow osm:
1459 self.logger.debug("creating firewall rule to allow osm")
1460 firewall_rule_body = {
1461 "name": "fw-rule-osm-" + network,
1462 "network": "global/networks/" + network,
1463 "allowed": [{"IPProtocol": "tcp", "ports": ["9001", "9999"]}],
1464 }
1465 operation_firewall = (
1466 self.conn_compute.firewalls()
1467 .insert(project=self.project, body=firewall_rule_body)
1468 .execute()
1469 )
1470
1471 self.logger.debug(
1472 "_create_firewall_rules Return: list_rules %s", rules_list
1473 )
1474 return rules_list
1475 except Exception as e:
1476 self.logger.error(
1477 "Unable to create google cloud firewall rules for network '{}'".format(
1478 network
1479 )
1480 )
1481 self._format_vimconn_exception(e)
1482
1483 def _delete_firewall_rules(self, network):
1484 """
1485 Deletes the associated firewall rules to the network
1486 :param network.
1487 :return: a list with the names of the firewall rules
1488 """
1489 self.logger.debug("_delete_firewall_rules begin: network %s", network)
1490 try:
1491 rules_list = []
1492
1493 rules_list = (
1494 self.conn_compute.firewalls().list(project=self.project).execute()
1495 )
1496 for item in rules_list["items"]:
1497 if network == self._get_resource_name_from_resource_id(item["network"]):
1498 operation_firewall = (
1499 self.conn_compute.firewalls()
1500 .delete(project=self.project, firewall=item["name"])
1501 .execute()
1502 )
1503
1504 self.logger.debug("_delete_firewall_rules Return: list_rules %s", 0)
1505 return rules_list
1506 except Exception as e:
1507 self.logger.error(
1508 "Unable to delete google cloud firewall rules for network '{}'".format(
1509 network
1510 )
1511 )
1512 self._format_vimconn_exception(e)