| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | ## |
| tierno | 9202102 | 2018-09-12 16:29:23 +0200 | [diff] [blame] | 4 | # Copyright 2017 Telefonica Digital Spain S.L.U. |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 5 | # This file is part of ETSI OSM |
| 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 | |
| 18 | # License for the specific language governing permissions and limitations |
| 19 | # under the License. |
| 20 | # |
| 21 | # For those usages not covered by the Apache License, Version 2.0 please |
| 22 | # contact with: patent-office@telefonica.com |
| 23 | ## |
| 24 | |
| 25 | """ |
| 26 | vimconnector implements all the methods to interact with OpenNebula using the XML-RPC API. |
| 27 | """ |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 28 | __author__ = ( |
| 29 | "Jose Maria Carmona Perez,Juan Antonio Hernando Labajo, Emilio Abraham Garrido Garcia,Alberto Florez " |
| 30 | "Pages, Andres Pozo Munoz, Santiago Perez Marin, Onlife Networks Telefonica I+D Product Innovation " |
| 31 | ) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 32 | __date__ = "$13-dec-2017 11:09:29$" |
| sousaedu | e493e9b | 2021-02-09 15:30:01 +0100 | [diff] [blame] | 33 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 34 | from osm_ro_plugin import vimconn |
| sousaedu | e493e9b | 2021-02-09 15:30:01 +0100 | [diff] [blame] | 35 | import logging |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 36 | import requests |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 37 | |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 38 | # import logging |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 39 | import oca |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 40 | |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 41 | # import untangle |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 42 | import math |
| 43 | import random |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 44 | import pyone |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 45 | |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 46 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 47 | class vimconnector(vimconn.VimConnector): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 48 | def __init__( |
| 49 | self, |
| 50 | uuid, |
| 51 | name, |
| 52 | tenant_id, |
| 53 | tenant_name, |
| 54 | url, |
| 55 | url_admin=None, |
| 56 | user=None, |
| 57 | passwd=None, |
| 58 | log_level="DEBUG", |
| 59 | config={}, |
| 60 | persistent_info={}, |
| 61 | ): |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 62 | """Constructor of VIM |
| 63 | Params: |
| 64 | 'uuid': id asigned to this VIM |
| 65 | 'name': name assigned to this VIM, can be used for logging |
| 66 | 'tenant_id', 'tenant_name': (only one of them is mandatory) VIM tenant to be used |
| 67 | 'url_admin': (optional), url used for administrative tasks |
| 68 | 'user', 'passwd': credentials of the VIM user |
| 69 | 'log_level': provider if it should use a different log_level than the general one |
| 70 | 'config': dictionary with extra VIM information. This contains a consolidate version of general VIM config |
| 71 | at creation and particular VIM config at teh attachment |
| 72 | 'persistent_info': dict where the class can store information that will be available among class |
| 73 | destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an |
| 74 | empty dict. Useful to store login/tokens information for speed up communication |
| 75 | |
| 76 | Returns: Raise an exception is some needed parameter is missing, but it must not do any connectivity |
| 77 | check against the VIM |
| 78 | """ |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 79 | vimconn.VimConnector.__init__( |
| 80 | self, |
| 81 | uuid, |
| 82 | name, |
| 83 | tenant_id, |
| 84 | tenant_name, |
| 85 | url, |
| 86 | url_admin, |
| 87 | user, |
| 88 | passwd, |
| 89 | log_level, |
| 90 | config, |
| 91 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 92 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 93 | self.logger = logging.getLogger("ro.vim.openstack") |
| sousaedu | e493e9b | 2021-02-09 15:30:01 +0100 | [diff] [blame] | 94 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 95 | def _new_one_connection(self): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 96 | return pyone.OneServer(self.url, session=self.user + ":" + self.passwd) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 97 | |
| 98 | def new_tenant(self, tenant_name, tenant_description): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 99 | # """Adds a new tenant to VIM with this name and description, returns the tenant identifier""" |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 100 | try: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 101 | client = oca.Client(self.user + ":" + self.passwd, self.url) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 102 | group_list = oca.GroupPool(client) |
| 103 | user_list = oca.UserPool(client) |
| 104 | group_list.info() |
| 105 | user_list.info() |
| 106 | create_primarygroup = 1 |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 107 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 108 | # create group-tenant |
| 109 | for group in group_list: |
| 110 | if str(group.name) == str(tenant_name): |
| 111 | create_primarygroup = 0 |
| 112 | break |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 113 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 114 | if create_primarygroup == 1: |
| 115 | oca.Group.allocate(client, tenant_name) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 116 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 117 | group_list.info() |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 118 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 119 | # set to primary_group the tenant_group and oneadmin to secondary_group |
| 120 | for group in group_list: |
| 121 | if str(group.name) == str(tenant_name): |
| 122 | for user in user_list: |
| 123 | if str(user.name) == str(self.user): |
| 124 | if user.name == "oneadmin": |
| 125 | return str(0) |
| 126 | else: |
| 127 | self._add_secondarygroup(user.id, group.id) |
| 128 | user.chgrp(group.id) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 129 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 130 | return str(group.id) |
| 131 | except Exception as e: |
| 132 | self.logger.error("Create new tenant error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 133 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 134 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 135 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 136 | def delete_tenant(self, tenant_id): |
| 137 | """Delete a tenant from VIM. Returns the old tenant identifier""" |
| 138 | try: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 139 | client = oca.Client(self.user + ":" + self.passwd, self.url) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 140 | group_list = oca.GroupPool(client) |
| 141 | user_list = oca.UserPool(client) |
| 142 | group_list.info() |
| 143 | user_list.info() |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 144 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 145 | for group in group_list: |
| 146 | if str(group.id) == str(tenant_id): |
| 147 | for user in user_list: |
| 148 | if str(user.name) == str(self.user): |
| 149 | self._delete_secondarygroup(user.id, group.id) |
| 150 | group.delete(client) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 151 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 152 | return None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 153 | |
| 154 | raise vimconn.VimConnNotFoundException( |
| 155 | "Group {} not found".format(tenant_id) |
| 156 | ) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 157 | except Exception as e: |
| 158 | self.logger.error("Delete tenant " + str(tenant_id) + " error: " + str(e)) |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 159 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 160 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 161 | def _add_secondarygroup(self, id_user, id_group): |
| 162 | # change secondary_group to primary_group |
| 163 | params = '<?xml version="1.0"?> \ |
| 164 | <methodCall>\ |
| 165 | <methodName>one.user.addgroup</methodName>\ |
| 166 | <params>\ |
| 167 | <param>\ |
| 168 | <value><string>{}:{}</string></value>\ |
| 169 | </param>\ |
| 170 | <param>\ |
| 171 | <value><int>{}</int></value>\ |
| 172 | </param>\ |
| 173 | <param>\ |
| 174 | <value><int>{}</int></value>\ |
| 175 | </param>\ |
| 176 | </params>\ |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 177 | </methodCall>'.format( |
| 178 | self.user, self.passwd, (str(id_user)), (str(id_group)) |
| 179 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 180 | requests.post(self.url, params) |
| 181 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 182 | def _delete_secondarygroup(self, id_user, id_group): |
| 183 | params = '<?xml version="1.0"?> \ |
| 184 | <methodCall>\ |
| 185 | <methodName>one.user.delgroup</methodName>\ |
| 186 | <params>\ |
| 187 | <param>\ |
| 188 | <value><string>{}:{}</string></value>\ |
| 189 | </param>\ |
| 190 | <param>\ |
| 191 | <value><int>{}</int></value>\ |
| 192 | </param>\ |
| 193 | <param>\ |
| 194 | <value><int>{}</int></value>\ |
| 195 | </param>\ |
| 196 | </params>\ |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 197 | </methodCall>'.format( |
| 198 | self.user, self.passwd, (str(id_user)), (str(id_group)) |
| 199 | ) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 200 | requests.post(self.url, params) |
| 201 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 202 | def new_network( |
| 203 | self, |
| 204 | net_name, |
| 205 | net_type, |
| 206 | ip_profile=None, |
| 207 | shared=False, |
| 208 | provider_network_profile=None, |
| 209 | ): |
| garciadeblas | ebd6672 | 2019-01-31 16:01:31 +0000 | [diff] [blame] | 210 | """Adds a tenant network to VIM |
| 211 | Params: |
| 212 | 'net_name': name of the network |
| 213 | 'net_type': one of: |
| 214 | 'bridge': overlay isolated network |
| 215 | 'data': underlay E-LAN network for Passthrough and SRIOV interfaces |
| 216 | 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces. |
| 217 | 'ip_profile': is a dict containing the IP parameters of the network |
| 218 | 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented) |
| 219 | 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y |
| 220 | 'gateway_address': (Optional) ip_schema, that is X.X.X.X |
| 221 | 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X] |
| 222 | 'dhcp_enabled': True or False |
| 223 | 'dhcp_start_address': ip_schema, first IP to grant |
| 224 | 'dhcp_count': number of IPs to grant. |
| 225 | 'shared': if this network can be seen/use by other tenants/organization |
| kbsub | a85c54d | 2019-10-17 16:30:32 +0000 | [diff] [blame] | 226 | 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk} |
| garciadeblas | ebd6672 | 2019-01-31 16:01:31 +0000 | [diff] [blame] | 227 | Returns a tuple with the network identifier and created_items, or raises an exception on error |
| 228 | created_items can be None or a dictionary where this method can include key-values that will be passed to |
| 229 | the method delete_network. Can be used to store created segments, created l2gw connections, etc. |
| 230 | Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same |
| 231 | as not present. |
| 232 | """ |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 233 | # oca library method cannot be used in this case (problem with cluster parameters) |
| 234 | try: |
| kbsub | a85c54d | 2019-10-17 16:30:32 +0000 | [diff] [blame] | 235 | vlan = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 236 | |
| kbsub | a85c54d | 2019-10-17 16:30:32 +0000 | [diff] [blame] | 237 | if provider_network_profile: |
| 238 | vlan = provider_network_profile.get("segmentation-id") |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 239 | |
| albertoflorez | 3ade00b | 2019-07-08 13:16:21 +0200 | [diff] [blame] | 240 | created_items = {} |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 241 | one = self._new_one_connection() |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 242 | size = "254" |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 243 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 244 | if ip_profile is None: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 245 | subnet_rand = random.randint(0, 255) |
| 246 | ip_start = "192.168.{}.1".format(subnet_rand) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 247 | else: |
| 248 | index = ip_profile["subnet_address"].find("/") |
| 249 | ip_start = ip_profile["subnet_address"][:index] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 250 | |
| tierno | 7d782ef | 2019-10-04 12:56:31 +0000 | [diff] [blame] | 251 | if "dhcp_count" in ip_profile and ip_profile["dhcp_count"] is not None: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 252 | size = str(ip_profile["dhcp_count"]) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 253 | elif ( |
| 254 | "dhcp_count" not in ip_profile |
| 255 | and ip_profile["ip_version"] == "IPv4" |
| 256 | ): |
| 257 | prefix = ip_profile["subnet_address"][index + 1 :] |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 258 | size = int(math.pow(2, 32 - prefix)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 259 | |
| 260 | if ( |
| 261 | "dhcp_start_address" in ip_profile |
| 262 | and ip_profile["dhcp_start_address"] is not None |
| 263 | ): |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 264 | ip_start = str(ip_profile["dhcp_start_address"]) |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 265 | # if ip_profile["ip_version"] == "IPv6": |
| 266 | # ip_prefix_type = "GLOBAL_PREFIX" |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 267 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 268 | if vlan is not None: |
| 269 | vlan_id = vlan |
| 270 | else: |
| 271 | vlan_id = str(random.randint(100, 4095)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 272 | |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 273 | # if "internal" in net_name: |
| albertoflorez | 3ade00b | 2019-07-08 13:16:21 +0200 | [diff] [blame] | 274 | # OpenNebula not support two networks with same name |
| 275 | random_net_name = str(random.randint(1, 1000000)) |
| 276 | net_name = net_name + random_net_name |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 277 | net_id = one.vn.allocate( |
| 278 | { |
| 279 | "NAME": net_name, |
| 280 | "VN_MAD": "802.1Q", |
| 281 | "PHYDEV": self.config["network"]["phydev"], |
| 282 | "VLAN_ID": vlan_id, |
| 283 | }, |
| 284 | self.config["cluster"]["id"], |
| 285 | ) |
| 286 | arpool = {"AR_POOL": {"AR": {"TYPE": "IP4", "IP": ip_start, "SIZE": size}}} |
| albertoflorez | 3ade00b | 2019-07-08 13:16:21 +0200 | [diff] [blame] | 287 | one.vn.add_ar(net_id, arpool) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 288 | |
| albertoflorez | 3ade00b | 2019-07-08 13:16:21 +0200 | [diff] [blame] | 289 | return net_id, created_items |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 290 | except Exception as e: |
| 291 | self.logger.error("Create new network error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 292 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 293 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 294 | |
| 295 | def get_network_list(self, filter_dict={}): |
| 296 | """Obtain tenant networks of VIM |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 297 | :params filter_dict: (optional) contains entries to return only networks that matches ALL entries: |
| 298 | name: string => returns only networks with this name |
| 299 | id: string => returns networks with this VIM id, this imply returns one network at most |
| 300 | shared: boolean >= returns only networks that are (or are not) shared |
| 301 | tenant_id: sting => returns only networks that belong to this tenant/project |
| 302 | (not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active |
| 303 | (not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 304 | Returns the network list of dictionaries. each dictionary contains: |
| 305 | 'id': (mandatory) VIM network id |
| 306 | 'name': (mandatory) VIM network name |
| 307 | 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER' |
| 308 | 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat' |
| 309 | 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id |
| 310 | 'error_msg': (optional) text that explains the ERROR status |
| 311 | other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param |
| 312 | List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity, |
| 313 | authorization, or some other unspecific error |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 314 | """ |
| 315 | try: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 316 | one = self._new_one_connection() |
| 317 | net_pool = one.vnpool.info(-2, -1, -1).VNET |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 318 | response = [] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 319 | |
| tierno | 7d782ef | 2019-10-04 12:56:31 +0000 | [diff] [blame] | 320 | if "name" in filter_dict: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 321 | network_name_filter = filter_dict["name"] |
| 322 | else: |
| 323 | network_name_filter = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 324 | |
| tierno | 7d782ef | 2019-10-04 12:56:31 +0000 | [diff] [blame] | 325 | if "id" in filter_dict: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 326 | network_id_filter = filter_dict["id"] |
| 327 | else: |
| 328 | network_id_filter = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 329 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 330 | for network in net_pool: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 331 | if network.NAME == network_name_filter or str(network.ID) == str( |
| 332 | network_id_filter |
| 333 | ): |
| 334 | net_dict = { |
| 335 | "name": network.NAME, |
| 336 | "id": str(network.ID), |
| 337 | "status": "ACTIVE", |
| 338 | } |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 339 | response.append(net_dict) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 340 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 341 | return response |
| 342 | except Exception as e: |
| 343 | self.logger.error("Get network list error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 344 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 345 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 346 | |
| 347 | def get_network(self, net_id): |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 348 | """Obtain network details from the 'net_id' VIM network |
| 349 | Return a dict that contains: |
| 350 | 'id': (mandatory) VIM network id, that is, net_id |
| 351 | 'name': (mandatory) VIM network name |
| 352 | 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER' |
| 353 | 'error_msg': (optional) text that explains the ERROR status |
| 354 | other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param |
| 355 | Raises an exception upon error or when network is not found |
| 356 | """ |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 357 | try: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 358 | one = self._new_one_connection() |
| 359 | net_pool = one.vnpool.info(-2, -1, -1).VNET |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 360 | net = {} |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 361 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 362 | for network in net_pool: |
| 363 | if str(network.ID) == str(net_id): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 364 | net["id"] = network.ID |
| 365 | net["name"] = network.NAME |
| 366 | net["status"] = "ACTIVE" |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 367 | break |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 368 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 369 | if net: |
| 370 | return net |
| 371 | else: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 372 | raise vimconn.VimConnNotFoundException( |
| 373 | "Network {} not found".format(net_id) |
| 374 | ) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 375 | except Exception as e: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 376 | self.logger.error("Get network " + str(net_id) + " error): " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 377 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 378 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 379 | |
| garciadeblas | ebd6672 | 2019-01-31 16:01:31 +0000 | [diff] [blame] | 380 | def delete_network(self, net_id, created_items=None): |
| 381 | """ |
| 382 | Removes a tenant network from VIM and its associated elements |
| 383 | :param net_id: VIM identifier of the network, provided by method new_network |
| 384 | :param created_items: dictionary with extra items to be deleted. provided by method new_network |
| 385 | Returns the network identifier or raises an exception upon error or when network is not found |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 386 | """ |
| 387 | try: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 388 | one = self._new_one_connection() |
| 389 | one.vn.delete(int(net_id)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 390 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 391 | return net_id |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 392 | except Exception as e: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 393 | self.logger.error( |
| 394 | "Delete network " + str(net_id) + "error: network not found" + str(e) |
| 395 | ) |
| 396 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 397 | raise vimconn.VimConnException(e) |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 398 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 399 | def refresh_nets_status(self, net_list): |
| 400 | """Get the status of the networks |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 401 | Params: |
| 402 | 'net_list': a list with the VIM network id to be get the status |
| 403 | Returns a dictionary with: |
| 404 | 'net_id': #VIM id of this network |
| 405 | status: #Mandatory. Text with one of: |
| 406 | # DELETED (not found at vim) |
| 407 | # VIM_ERROR (Cannot connect to VIM, authentication problems, VIM response error, ...) |
| 408 | # OTHER (Vim reported other status not understood) |
| 409 | # ERROR (VIM indicates an ERROR status) |
| 410 | # ACTIVE, INACTIVE, DOWN (admin down), |
| 411 | # BUILD (on building process) |
| 412 | error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR |
| 413 | vim_info: #Text with plain information obtained from vim (yaml.safe_dump) |
| 414 | 'net_id2': ... |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 415 | """ |
| 416 | net_dict = {} |
| 417 | try: |
| 418 | for net_id in net_list: |
| 419 | net = {} |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 420 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 421 | try: |
| 422 | net_vim = self.get_network(net_id) |
| 423 | net["status"] = net_vim["status"] |
| 424 | net["vim_info"] = None |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 425 | except vimconn.VimConnNotFoundException as e: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 426 | self.logger.error("Exception getting net status: {}".format(str(e))) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 427 | net["status"] = "DELETED" |
| 428 | net["error_msg"] = str(e) |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 429 | except vimconn.VimConnException as e: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 430 | self.logger.error(e) |
| 431 | net["status"] = "VIM_ERROR" |
| 432 | net["error_msg"] = str(e) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 433 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 434 | net_dict[net_id] = net |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 435 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 436 | return net_dict |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 437 | except vimconn.VimConnException as e: |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 438 | self.logger.error(e) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 439 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 440 | for k in net_dict: |
| 441 | net_dict[k]["status"] = "VIM_ERROR" |
| 442 | net_dict[k]["error_msg"] = str(e) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 443 | |
| jomacarpe | ea2a73e | 2018-02-27 13:48:22 +0100 | [diff] [blame] | 444 | return net_dict |
| 445 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 446 | def get_flavor(self, flavor_id): # Esta correcto |
| 447 | """Obtain flavor details from the VIM |
| 448 | Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } |
| 449 | Raises an exception upon error or if not found |
| 450 | """ |
| 451 | try: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 452 | one = self._new_one_connection() |
| 453 | template = one.template.info(int(flavor_id)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 454 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 455 | if template is not None: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 456 | return {"id": template.ID, "name": template.NAME} |
| 457 | |
| 458 | raise vimconn.VimConnNotFoundException( |
| 459 | "Flavor {} not found".format(flavor_id) |
| 460 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 461 | except Exception as e: |
| 462 | self.logger.error("get flavor " + str(flavor_id) + " error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 463 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 464 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 465 | |
| 466 | def new_flavor(self, flavor_data): |
| 467 | """Adds a tenant flavor to VIM |
| 468 | flavor_data contains a dictionary with information, keys: |
| 469 | name: flavor name |
| 470 | ram: memory (cloud type) in MBytes |
| 471 | vpcus: cpus (cloud type) |
| 472 | extended: EPA parameters |
| 473 | - numas: #items requested in same NUMA |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 474 | memory: number of 1G huge pages memory |
| 475 | paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads |
| 476 | interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 477 | - name: interface name |
| 478 | dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC |
| 479 | bandwidth: X Gbps; requested guarantee bandwidth |
| 480 | vpci: requested virtual PCI address |
| 481 | disk: disk size |
| 482 | is_public: |
| 483 | #TODO to concrete |
| 484 | Returns the flavor identifier""" |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 485 | disk_size = str(int(flavor_data["disk"]) * 1024) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 486 | |
| 487 | try: |
| 488 | one = self._new_one_connection() |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 489 | template_id = one.template.allocate( |
| 490 | { |
| 491 | "TEMPLATE": { |
| 492 | "NAME": flavor_data["name"], |
| 493 | "CPU": flavor_data["vcpus"], |
| 494 | "VCPU": flavor_data["vcpus"], |
| 495 | "MEMORY": flavor_data["ram"], |
| 496 | "DISK": {"SIZE": disk_size}, |
| 497 | "CONTEXT": { |
| 498 | "NETWORK": "YES", |
| 499 | "SSH_PUBLIC_KEY": "$USER[SSH_PUBLIC_KEY]", |
| 500 | }, |
| 501 | "GRAPHICS": {"LISTEN": "0.0.0.0", "TYPE": "VNC"}, |
| 502 | "CLUSTER_ID": self.config["cluster"]["id"], |
| 503 | } |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 504 | } |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 505 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 506 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 507 | return template_id |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 508 | except Exception as e: |
| 509 | self.logger.error("Create new flavor error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 510 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 511 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 512 | |
| 513 | def delete_flavor(self, flavor_id): |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 514 | """Deletes a tenant flavor from VIM |
| 515 | Returns the old flavor_id |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 516 | """ |
| 517 | try: |
| 518 | one = self._new_one_connection() |
| 519 | one.template.delete(int(flavor_id), False) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 520 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 521 | return flavor_id |
| 522 | except Exception as e: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 523 | self.logger.error( |
| 524 | "Error deleting flavor " + str(flavor_id) + ". Flavor not found" |
| 525 | ) |
| 526 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 527 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 528 | |
| 529 | def get_image_list(self, filter_dict={}): |
| 530 | """Obtain tenant images from VIM |
| 531 | Filter_dict can be: |
| 532 | name: image name |
| 533 | id: image uuid |
| 534 | checksum: image checksum |
| 535 | location: image path |
| 536 | Returns the image list of dictionaries: |
| 537 | [{<the fields at Filter_dict plus some VIM specific>}, ...] |
| 538 | List can be empty |
| 539 | """ |
| 540 | try: |
| 541 | one = self._new_one_connection() |
| 542 | image_pool = one.imagepool.info(-2, -1, -1).IMAGE |
| 543 | images = [] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 544 | |
| tierno | 7d782ef | 2019-10-04 12:56:31 +0000 | [diff] [blame] | 545 | if "name" in filter_dict: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 546 | image_name_filter = filter_dict["name"] |
| 547 | else: |
| 548 | image_name_filter = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 549 | |
| tierno | 7d782ef | 2019-10-04 12:56:31 +0000 | [diff] [blame] | 550 | if "id" in filter_dict: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 551 | image_id_filter = filter_dict["id"] |
| 552 | else: |
| 553 | image_id_filter = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 554 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 555 | for image in image_pool: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 556 | if str(image_name_filter) == str(image.NAME) or str(image.ID) == str( |
| 557 | image_id_filter |
| 558 | ): |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 559 | images_dict = {"name": image.NAME, "id": str(image.ID)} |
| 560 | images.append(images_dict) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 561 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 562 | return images |
| 563 | except Exception as e: |
| 564 | self.logger.error("Get image list error: " + str(e)) |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 565 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 566 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 567 | def new_vminstance( |
| 568 | self, |
| 569 | name, |
| 570 | description, |
| 571 | start, |
| 572 | image_id, |
| 573 | flavor_id, |
| 574 | net_list, |
| 575 | cloud_config=None, |
| 576 | disk_list=None, |
| 577 | availability_zone_index=None, |
| 578 | availability_zone_list=None, |
| 579 | ): |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 580 | """ |
| 581 | Adds a VM instance to VIM |
| 582 | :param name: |
| 583 | :param description: |
| 584 | :param start: (boolean) indicates if VM must start or created in pause mode. |
| 585 | :param image_id: image VIM id to use for the VM |
| 586 | :param flavor_id: flavor VIM id to use for the VM |
| 587 | :param net_list: list of interfaces, each one is a dictionary with: |
| 588 | 'name': (optional) name for the interface. |
| 589 | 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual |
| 590 | 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM |
| 591 | capabilities |
| 592 | 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ... |
| 593 | 'mac_address': (optional) mac address to assign to this interface |
| 594 | 'ip_address': (optional) IP address to assign to this interface |
| 595 | #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not |
| 596 | provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is |
| 597 | used for tagging VF |
| 598 | 'type': (mandatory) can be one of: |
| 599 | 'virtual', in this case always connected to a network of type 'net_type=bridge' |
| 600 | 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to |
| 601 | a data/ptp network ot itcan created unconnected |
| 602 | 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity. |
| 603 | 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs |
| 604 | are allocated on the same physical NIC |
| 605 | 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS |
| 606 | 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing |
| 607 | or True, it must apply the default VIM behaviour |
| 608 | After execution the method will add the key: |
| 609 | 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this |
| 610 | interface. 'net_list' is modified |
| 611 | :param cloud_config: (optional) dictionary with: |
| 612 | 'key-pairs': (optional) list of strings with the public key to be inserted to the default user |
| 613 | 'users': (optional) list of users to be inserted, each item is a dict with: |
| 614 | 'name': (mandatory) user name, |
| 615 | 'key-pairs': (optional) list of strings with the public key to be inserted to the user |
| 616 | 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init, |
| 617 | or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file |
| 618 | 'config-files': (optional). List of files to be transferred. Each item is a dict with: |
| 619 | 'dest': (mandatory) string with the destination absolute path |
| 620 | 'encoding': (optional, by default text). Can be one of: |
| 621 | 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64' |
| 622 | 'content' (mandatory): string with the content of the file |
| 623 | 'permissions': (optional) string with file permissions, typically octal notation '0644' |
| 624 | 'owner': (optional) file owner, string with the format 'owner:group' |
| 625 | 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk) |
| 626 | :param disk_list: (optional) list with additional disks to the VM. Each item is a dict with: |
| 627 | 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted |
| 628 | 'size': (mandatory) string with the size of the disk in GB |
| 629 | :param availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV |
| 630 | required |
| 631 | :param availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 632 | availability_zone_index is None |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 633 | :return: a tuple with the instance identifier and created_items or raises an exception on error |
| 634 | created_items can be None or a dictionary where this method can include key-values that will be passed to |
| 635 | the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc. |
| 636 | Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same |
| 637 | as not present. |
| 638 | """ |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 639 | self.logger.debug( |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 640 | "new_vminstance input: image='{}' flavor='{}' nics='{}'".format( |
| 641 | image_id, flavor_id, str(net_list) |
| 642 | ) |
| 643 | ) |
| 644 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 645 | try: |
| 646 | one = self._new_one_connection() |
| 647 | template_vim = one.template.info(int(flavor_id), True) |
| 648 | disk_size = str(template_vim.TEMPLATE["DISK"]["SIZE"]) |
| 649 | |
| 650 | one = self._new_one_connection() |
| 651 | template_updated = "" |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 652 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 653 | for net in net_list: |
| 654 | net_in_vim = one.vn.info(int(net["net_id"])) |
| 655 | net["vim_id"] = str(net_in_vim.ID) |
| 656 | network = 'NIC = [NETWORK = "{}",NETWORK_UNAME = "{}" ]'.format( |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 657 | net_in_vim.NAME, net_in_vim.UNAME |
| 658 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 659 | template_updated += network |
| 660 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 661 | template_updated += "DISK = [ IMAGE_ID = {},\n SIZE = {}]".format( |
| 662 | image_id, disk_size |
| 663 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 664 | |
| 665 | if isinstance(cloud_config, dict): |
| 666 | if cloud_config.get("key-pairs"): |
| 667 | context = 'CONTEXT = [\n NETWORK = "YES",\n SSH_PUBLIC_KEY = "' |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 668 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 669 | for key in cloud_config["key-pairs"]: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 670 | context += key + "\n" |
| 671 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 672 | # if False: |
| 673 | # context += '"\n USERNAME = ' |
| 674 | context += '"]' |
| 675 | template_updated += context |
| 676 | |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 677 | vm_instance_id = one.template.instantiate( |
| 678 | int(flavor_id), name, False, template_updated |
| 679 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 680 | self.logger.info( |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 681 | "Instanciating in OpenNebula a new VM name:{} id:{}".format( |
| 682 | name, flavor_id |
| 683 | ) |
| 684 | ) |
| 685 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 686 | return str(vm_instance_id), None |
| 687 | except pyone.OneNoExistsException as e: |
| 688 | self.logger.error("Network with id " + str(e) + " not found: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 689 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 690 | raise vimconn.VimConnNotFoundException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 691 | except Exception as e: |
| 692 | self.logger.error("Create new vm instance error: " + str(e)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 693 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 694 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 695 | |
| 696 | def get_vminstance(self, vm_id): |
| 697 | """Returns the VM instance information from VIM""" |
| 698 | try: |
| 699 | one = self._new_one_connection() |
| 700 | vm = one.vm.info(int(vm_id)) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 701 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 702 | return vm |
| 703 | except Exception as e: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 704 | self.logger.error( |
| 705 | "Getting vm instance error: " + str(e) + ": VM Instance not found" |
| 706 | ) |
| 707 | |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 708 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 709 | |
| 710 | def delete_vminstance(self, vm_id, created_items=None): |
| 711 | """ |
| 712 | Removes a VM instance from VIM and its associated elements |
| 713 | :param vm_id: VIM identifier of the VM, provided by method new_vminstance |
| 714 | :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method |
| 715 | action_vminstance |
| 716 | :return: None or the same vm_id. Raises an exception on fail |
| 717 | """ |
| 718 | try: |
| 719 | one = self._new_one_connection() |
| 720 | one.vm.recover(int(vm_id), 3) |
| 721 | vm = None |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 722 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 723 | while True: |
| 724 | if vm is not None and vm.LCM_STATE == 0: |
| 725 | break |
| 726 | else: |
| 727 | vm = one.vm.info(int(vm_id)) |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 728 | except pyone.OneNoExistsException: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 729 | self.logger.info( |
| 730 | "The vm " + str(vm_id) + " does not exist or is already deleted" |
| 731 | ) |
| 732 | |
| 733 | raise vimconn.VimConnNotFoundException( |
| 734 | "The vm {} does not exist or is already deleted".format(vm_id) |
| 735 | ) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 736 | except Exception as e: |
| 737 | self.logger.error("Delete vm instance " + str(vm_id) + " error: " + str(e)) |
| tierno | 7277486 | 2020-05-04 11:44:15 +0000 | [diff] [blame] | 738 | raise vimconn.VimConnException(e) |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 739 | |
| 740 | def refresh_vms_status(self, vm_list): |
| 741 | """Get the status of the virtual machines and their interfaces/ports |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 742 | Params: the list of VM identifiers |
| 743 | Returns a dictionary with: |
| 744 | vm_id: #VIM id of this Virtual Machine |
| 745 | status: #Mandatory. Text with one of: |
| 746 | # DELETED (not found at vim) |
| 747 | # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) |
| 748 | # OTHER (Vim reported other status not understood) |
| 749 | # ERROR (VIM indicates an ERROR status) |
| 750 | # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running), |
| 751 | # BUILD (on building process), ERROR |
| 752 | # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address |
| 753 | # |
| 754 | error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR |
| 755 | vim_info: #Text with plain information obtained from vim (yaml.safe_dump) |
| 756 | interfaces: list with interface info. Each item a dictionary with: |
| 757 | vim_info: #Text with plain information obtained from vim (yaml.safe_dump) |
| 758 | mac_address: #Text format XX:XX:XX:XX:XX:XX |
| 759 | vim_net_id: #network id where this interface is connected, if provided at creation |
| 760 | vim_interface_id: #interface/port VIM id |
| 761 | ip_address: #null, or text with IPv4, IPv6 address |
| 762 | compute_node: #identification of compute node where PF,VF interface is allocated |
| 763 | pci: #PCI address of the NIC that hosts the PF,VF |
| 764 | vlan: #physical VLAN used for VF |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 765 | """ |
| 766 | vm_dict = {} |
| 767 | try: |
| 768 | for vm_id in vm_list: |
| 769 | vm = {} |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 770 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 771 | if self.get_vminstance(vm_id) is not None: |
| 772 | vm_element = self.get_vminstance(vm_id) |
| 773 | else: |
| 774 | self.logger.info("The vm " + str(vm_id) + " does not exist.") |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 775 | vm["status"] = "DELETED" |
| 776 | vm["error_msg"] = "The vm " + str(vm_id) + " does not exist." |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 777 | continue |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 778 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 779 | vm["vim_info"] = None |
| 780 | vm_status = vm_element.LCM_STATE |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 781 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 782 | if vm_status == 3: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 783 | vm["status"] = "ACTIVE" |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 784 | elif vm_status == 36: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 785 | vm["status"] = "ERROR" |
| 786 | vm["error_msg"] = "VM failure" |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 787 | else: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 788 | vm["status"] = "BUILD" |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 789 | |
| 790 | if vm_element is not None: |
| 791 | interfaces = self._get_networks_vm(vm_element) |
| 792 | vm["interfaces"] = interfaces |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 793 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 794 | vm_dict[vm_id] = vm |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 795 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 796 | return vm_dict |
| 797 | except Exception as e: |
| 798 | self.logger.error(e) |
| 799 | for k in vm_dict: |
| 800 | vm_dict[k]["status"] = "VIM_ERROR" |
| 801 | vm_dict[k]["error_msg"] = str(e) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 802 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 803 | return vm_dict |
| 804 | |
| 805 | def _get_networks_vm(self, vm_element): |
| 806 | interfaces = [] |
| 807 | try: |
| 808 | if isinstance(vm_element.TEMPLATE["NIC"], list): |
| 809 | for net in vm_element.TEMPLATE["NIC"]: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 810 | interface = { |
| 811 | "vim_info": None, |
| 812 | "mac_address": str(net["MAC"]), |
| 813 | "vim_net_id": str(net["NETWORK_ID"]), |
| 814 | "vim_interface_id": str(net["NETWORK_ID"]), |
| 815 | } |
| 816 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 817 | # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6 |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 818 | if "IP" in net: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 819 | interface["ip_address"] = str(net["IP"]) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 820 | |
| 821 | if "IP6_GLOBAL" in net: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 822 | interface["ip_address"] = str(net["IP6_GLOBAL"]) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 823 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 824 | interfaces.append(interface) |
| 825 | else: |
| 826 | net = vm_element.TEMPLATE["NIC"] |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 827 | interface = { |
| 828 | "vim_info": None, |
| 829 | "mac_address": str(net["MAC"]), |
| 830 | "vim_net_id": str(net["NETWORK_ID"]), |
| 831 | "vim_interface_id": str(net["NETWORK_ID"]), |
| 832 | } |
| 833 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 834 | # maybe it should be 2 different keys for ip_address if an interface has ipv4 and ipv6 |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 835 | if "IP" in net: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 836 | interface["ip_address"] = str(net["IP"]) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 837 | |
| 838 | if "IP6_GLOBAL" in net: |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 839 | interface["ip_address"] = str(net["IP6_GLOBAL"]) |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 840 | |
| jomacarpe | 20c1323 | 2019-06-12 21:20:51 +0000 | [diff] [blame] | 841 | interfaces.append(interface) |
| 842 | return interfaces |
| tierno | 1ec592d | 2020-06-16 15:29:47 +0000 | [diff] [blame] | 843 | except Exception: |
| sousaedu | 80135b9 | 2021-02-17 15:05:18 +0100 | [diff] [blame] | 844 | self.logger.error( |
| 845 | "Error getting vm interface_information of vm_id: " + str(vm_element.ID) |
| 846 | ) |