Fix bug 1859 for VNET supporting in a different resource group
[osm/RO.git] / RO-VIM-azure / osm_rovim_azure / vimconn_azure.py
index 71c7f59..967eb3d 100755 (executable)
 ##
 
 import base64
-from osm_ro_plugin import vimconn
 import logging
-import netaddr
+from os import getenv
 import re
 
-from os import getenv
+from azure.core.exceptions import ResourceNotFoundError
 from azure.identity import ClientSecretCredential
-from azure.mgmt.resource import ResourceManagementClient
-from azure.mgmt.network import NetworkManagementClient
 from azure.mgmt.compute import ComputeManagementClient
 from azure.mgmt.compute.models import DiskCreateOption
-from azure.core.exceptions import ResourceNotFoundError
+from azure.mgmt.network import NetworkManagementClient
+from azure.mgmt.resource import ResourceManagementClient
 from azure.profiles import ProfileDefinition
-from msrestazure.azure_exceptions import CloudError
+from cryptography.hazmat.backends import default_backend as crypto_default_backend
+from cryptography.hazmat.primitives import serialization as crypto_serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
 from msrest.exceptions import AuthenticationError
+from msrestazure.azure_exceptions import CloudError
 import msrestazure.tools as azure_tools
+import netaddr
+from osm_ro_plugin import vimconn
 from requests.exceptions import ConnectionError
 
-from cryptography.hazmat.primitives import serialization as crypto_serialization
-from cryptography.hazmat.primitives.asymmetric import rsa
-from cryptography.hazmat.backends import default_backend as crypto_default_backend
-
 __author__ = "Isabel Lloret, Sergio Gonzalez, Alfonso Tierno, Gerardo Garcia"
 __date__ = "$18-apr-2019 23:59:59$"
 
@@ -49,6 +48,14 @@ if getenv("OSMRO_PDB_DEBUG"):
     pdb.set_trace()
 
 
+def find_in_list(the_list, condition_lambda):
+    for item in the_list:
+        if condition_lambda(item):
+            return item
+    else:
+        return None
+
+
 class vimconnector(vimconn.VimConnector):
 
     # Translate azure provisioning state to OSM provision state
@@ -221,14 +228,6 @@ class vimconnector(vimconn.VimConnector):
         else:
             raise vimconn.VimConnException("Subscription not specified")
 
-        # REGION
-        if "region_name" in config:
-            self.region = config.get("region_name")
-        else:
-            raise vimconn.VimConnException(
-                "Azure region_name is not specified at config"
-            )
-
         # RESOURCE_GROUP
         if "resource_group" in config:
             self.resource_group = config.get("resource_group")
@@ -237,10 +236,21 @@ class vimconnector(vimconn.VimConnector):
                 "Azure resource_group is not specified at config"
             )
 
+        # REGION
+        if "region_name" in config:
+            self.region = config.get("region_name")
+        else:
+            raise vimconn.VimConnException(
+                "Azure region_name is not specified at config"
+            )
+
         # VNET_NAME
         if "vnet_name" in config:
             self.vnet_name = config["vnet_name"]
 
+        # VNET_RESOURCE_GROUP
+        self.vnet_resource_group = config.get("vnet_resource_group")
+
         # TODO - not used, do anything about it?
         # public ssh key
         self.pub_key = config.get("pub_key")
@@ -253,6 +263,13 @@ class vimconnector(vimconn.VimConnector):
         if "flavors_pattern" in config:
             self._config["flavors_pattern"] = config["flavors_pattern"]
 
+    def _find_in_capabilities(self, capabilities, name):
+        cap = find_in_list(capabilities, lambda c: c["name"] == name)
+        if cap:
+            return cap.get("value")
+        else:
+            return None
+
     def _reload_connection(self):
         """
         Called before any operation, checks python azure clients
@@ -390,7 +407,7 @@ class vimconnector(vimconn.VimConnector):
         """
         try:
             vnet = self.conn_vnet.virtual_networks.get(
-                self.resource_group, self.vnet_name
+                self.vnet_resource_group or self.resource_group, self.vnet_name
             )
             self.vnet_address_space = vnet.address_space.address_prefixes[0]
             self.vnet_id = vnet.id
@@ -413,10 +430,12 @@ class vimconnector(vimconn.VimConnector):
 
             self.logger.debug("create base vnet: %s", self.vnet_name)
             self.conn_vnet.virtual_networks.begin_create_or_update(
-                self.resource_group, self.vnet_name, vnet_params
+                self.vnet_resource_group or self.resource_group,
+                self.vnet_name,
+                vnet_params,
             )
             vnet = self.conn_vnet.virtual_networks.get(
-                self.resource_group, self.vnet_name
+                self.vnet_resource_group or self.resource_group, self.vnet_name
             )
             self.vnet_id = vnet.id
         except Exception as e:
@@ -496,7 +515,10 @@ class vimconnector(vimconn.VimConnector):
 
             self.logger.debug("creating subnet_name: {}".format(subnet_name))
             async_creation = self.conn_vnet.subnets.begin_create_or_update(
-                self.resource_group, self.vnet_name, subnet_name, subnet_params
+                self.vnet_resource_group or self.resource_group,
+                self.vnet_name,
+                subnet_name,
+                subnet_params,
             )
             async_creation.wait()
             # TODO - do not wait here, check where it is used
@@ -511,7 +533,9 @@ class vimconnector(vimconn.VimConnector):
         Adds a prefix to the subnet_name with a number in case the indicated name is repeated
         Checks subnets with the indicated name (without suffix) and adds a suffix with a number
         """
-        all_subnets = self.conn_vnet.subnets.list(self.resource_group, self.vnet_name)
+        all_subnets = self.conn_vnet.subnets.list(
+            self.vnet_resource_group or self.resource_group, self.vnet_name
+        )
         # Filter to subnets starting with the indicated name
         subnets = list(
             filter(lambda subnet: (subnet.name.startswith(subnet_name)), all_subnets)
@@ -528,12 +552,15 @@ class vimconnector(vimconn.VimConnector):
 
         return name
 
-    def _create_nic(self, net, nic_name, static_ip=None, created_items={}):
+    def _create_nic(self, net, nic_name, region=None, static_ip=None, created_items={}):
         self.logger.debug("create nic name %s, net_name %s", nic_name, net)
         self._reload_connection()
 
         subnet_id = net["net_id"]
-        location = self._get_location_from_resource_group(self.resource_group)
+        location = self.region or self._get_location_from_resource_group(
+            self.resource_group
+        )
+
         try:
             net_ifz = {"location": location}
             net_ip_config = {
@@ -799,7 +826,7 @@ class vimconnector(vimconn.VimConnector):
             self._reload_connection()
 
             vnet = self.conn_vnet.virtual_networks.get(
-                self.resource_group, self.vnet_name
+                self.vnet_resource_group or self.resource_group, self.vnet_name
             )
             subnet_list = []
 
@@ -888,7 +915,7 @@ class vimconnector(vimconn.VimConnector):
                 # subnet_id=net["net_id"]
                 nic_name = vm_name + "-nic-" + str(idx)
                 vm_nic, nic_items = self._create_nic(
-                    net, nic_name, net.get("ip_address"), created_items
+                    net, nic_name, self.region, net.get("ip_address"), created_items
                 )
                 vm_nics.append({"id": str(vm_nic.id)})
                 net["vim_id"] = vm_nic.id
@@ -1223,9 +1250,9 @@ class vimconnector(vimconn.VimConnector):
         try:
             self._reload_connection()
             vm_sizes_list = [
-                vm_size.serialize()
+                vm_size.as_dict()
                 for vm_size in self.conn_compute.resource_skus.list(
-                    "location={}".format(self.region)
+                    "location eq '{}'".format(self.region)
                 )
             ]
 
@@ -1234,31 +1261,56 @@ class vimconnector(vimconn.VimConnector):
             numberInterfaces = len(filter_dict.get("interfaces", [])) or 0
 
             # Filter
-            if self._config.get("flavors_pattern"):
-                filtered_sizes = [
-                    size
-                    for size in vm_sizes_list
-                    if size["capabilities"]["vCPUs"] >= cpus
-                    and size["capabilities"]["MemoryGB"] >= memMB / 1024
-                    and size["capabilities"]["MaxNetworkInterfaces"] >= numberInterfaces
-                    and re.search(self._config.get("flavors_pattern"), size["name"])
-                ]
-            else:
-                filtered_sizes = [
-                    size
-                    for size in vm_sizes_list
-                    if size["capabilities"]["vCPUs"] >= cpus
-                    and size["capabilities"]["MemoryGB"] >= memMB / 1024
-                    and size["capabilities"]["MaxNetworkInterfaces"] >= numberInterfaces
-                ]
+            filtered_sizes = []
+            for size in vm_sizes_list:
+                if size["resource_type"] == "virtualMachines":
+                    size_cpus = int(
+                        self._find_in_capabilities(size["capabilities"], "vCPUs")
+                    )
+                    size_memory = float(
+                        self._find_in_capabilities(size["capabilities"], "MemoryGB")
+                    )
+                    size_interfaces = self._find_in_capabilities(
+                        size["capabilities"], "MaxNetworkInterfaces"
+                    )
+                    if size_interfaces:
+                        size_interfaces = int(size_interfaces)
+                    else:
+                        self.logger.debug(
+                            "Flavor with no defined MaxNetworkInterfaces: {}".format(
+                                size["name"]
+                            )
+                        )
+                        continue
+                    if (
+                        size_cpus >= cpus
+                        and size_memory >= memMB / 1024
+                        and size_interfaces >= numberInterfaces
+                    ):
+                        if self._config.get("flavors_pattern"):
+                            if re.search(
+                                self._config.get("flavors_pattern"), size["name"]
+                            ):
+                                new_size = {
+                                    e["name"]: e["value"] for e in size["capabilities"]
+                                }
+                                new_size["name"] = size["name"]
+                                filtered_sizes.append(new_size)
+                        else:
+                            new_size = {
+                                e["name"]: e["value"] for e in size["capabilities"]
+                            }
+                            new_size["name"] = size["name"]
+                            filtered_sizes.append(new_size)
 
             # Sort
             listedFilteredSizes = sorted(
                 filtered_sizes,
                 key=lambda k: (
-                    k["numberOfCores"],
-                    k["memoryInMB"],
-                    k["resourceDiskSizeInMB"],
+                    int(k["vCPUs"]),
+                    float(k["MemoryGB"]),
+                    int(k["MaxNetworkInterfaces"]),
+                    int(k["MaxResourceVolumeMB"]),
                 ),
             )
 
@@ -1276,9 +1328,9 @@ class vimconnector(vimconn.VimConnector):
         try:
             self._reload_connection()
             vm_sizes_list = [
-                vm_size.serialize()
+                vm_size.as_dict()
                 for vm_size in self.conn_compute.resource_skus.list(
-                    "location={}".format(self.region)
+                    "location eq '{}'".format(self.region)
                 )
             ]
 
@@ -1318,7 +1370,9 @@ class vimconnector(vimconn.VimConnector):
 
     def delete_network(self, net_id, created_items=None):
         self.logger.debug(
-            "deleting network {} - {}".format(self.resource_group, net_id)
+            "deleting network {} - {}".format(
+                self.vnet_resource_group or self.resource_group, net_id
+            )
         )
 
         self._reload_connection()
@@ -1327,7 +1381,9 @@ class vimconnector(vimconn.VimConnector):
         try:
             # Obtain subnets ant try to delete nic first
             subnet = self.conn_vnet.subnets.get(
-                self.resource_group, self.vnet_name, res_name
+                self.vnet_resource_group or self.resource_group,
+                self.vnet_name,
+                res_name,
             )
             if not subnet:
                 raise vimconn.VimConnNotFoundException(
@@ -1346,7 +1402,9 @@ class vimconnector(vimconn.VimConnector):
             # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
             # Put the initial virtual_network API
             async_delete = self.conn_vnet.subnets.begin_delete(
-                self.resource_group, self.vnet_name, res_name
+                self.vnet_resource_group or self.resource_group,
+                self.vnet_name,
+                res_name,
             )
             async_delete.wait()
 
@@ -1759,7 +1817,9 @@ class vimconnector(vimconn.VimConnector):
                 netName = self._get_net_name_from_resource_id(net_id)
                 resName = self._get_resource_name_from_resource_id(net_id)
 
-                net = self.conn_vnet.subnets.get(self.resource_group, netName, resName)
+                net = self.conn_vnet.subnets.get(
+                    self.vnet_resource_group or self.resource_group, netName, resName
+                )
 
                 out_nets[net_id] = {
                     "status": self.provision_state2osm[net.provisioning_state],
@@ -2004,7 +2064,7 @@ if __name__ == "__main__":
     logger.debug("Network_list: {}".format(network_list))
 
     logger.debug("List flavors")
-    flavors = azure.get_flavor_id_from_data({"vcpu": "2"})
+    flavors = azure.get_flavor_id_from_data({"vcpus": 2})
     logger.debug("flavors: {}".format(flavors))
     """
 
@@ -2025,7 +2085,7 @@ if __name__ == "__main__":
     logger.debug("List networks")
     network_list = azure.get_network_list({"name": "internal"})
     logger.debug("Network_list: {}".format(network_list))
-    
+
     logger.debug("Show machine isabelvm")
     vmachine = azure.get_vminstance( ("/subscriptions/5c1a2458-dfde-4adf-a4e3-08fa0e21d171/resourceGroups/{}"
                                       "/providers/Microsoft.Compute/virtualMachines/isabelVM"
@@ -2034,10 +2094,12 @@ if __name__ == "__main__":
     logger.debug("Vmachine: {}".format(vmachine))
     """
 
+    """
     logger.debug("List images")
     image = azure.get_image_list({"name": "Canonical:UbuntuServer:16.04"})
     # image = azure.get_image_list({"name": "Canonical:UbuntuServer:18.04-LTS"})
     logger.debug("image: {}".format(image))
+    """
 
     """
     # Create network and test machine
@@ -2047,7 +2109,7 @@ if __name__ == "__main__":
                 "/Skus/18.04-LTS/Versions/18.04.201809110")
     """
     """
-    
+
     network_id = ("subscriptions/5c1a2458-dfde-4adf-a4e3-08fa0e21d171/resourceGroups/{}
                   "/providers/Microsoft.Network/virtualNetworks/osm_vnet/subnets/internal"
                  ).format(test_params["resource_group"])