blob: 6e7059c518d87887cccb09d47a8876acc1f5226b [file] [log] [blame]
bayramov325fa1c2016-09-08 01:42:46 -07001# -*- coding: utf-8 -*-
2
3##
4# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact with: nfvlabs@tid.es
22##
23
bayramov5761ad12016-10-04 09:00:30 +040024"""
bayramov325fa1c2016-09-08 01:42:46 -070025vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
26mbayramov@vmware.com
bayramov5761ad12016-10-04 09:00:30 +040027"""
bayramovfe3f3c92016-10-04 07:53:41 +040028from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
bayramovbd6160f2016-09-28 04:12:05 +040029
30import vimconn
bayramov325fa1c2016-09-08 01:42:46 -070031import os
bayramovef390722016-09-27 03:34:46 -070032import traceback
bayramovef390722016-09-27 03:34:46 -070033import itertools
bayramov325fa1c2016-09-08 01:42:46 -070034import requests
bhangarefda5f7c2017-01-12 23:50:34 -080035import ssl
36import atexit
37
38from pyVmomi import vim, vmodl
39from pyVim.connect import SmartConnect, Disconnect
bayramov325fa1c2016-09-08 01:42:46 -070040
bayramovef390722016-09-27 03:34:46 -070041from xml.etree import ElementTree as XmlElementTree
bhangarea92ae392017-01-12 22:30:29 -080042from lxml import etree as lxmlElementTree
bayramov325fa1c2016-09-08 01:42:46 -070043
bayramovef390722016-09-27 03:34:46 -070044import yaml
bayramov325fa1c2016-09-08 01:42:46 -070045from pyvcloud import Http
46from pyvcloud.vcloudair import VCA
47from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
48 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
49 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
50from xml.sax.saxutils import escape
51
bayramovef390722016-09-27 03:34:46 -070052from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
53from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
54from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
55from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
56
bayramov325fa1c2016-09-08 01:42:46 -070057import logging
58import json
bayramov325fa1c2016-09-08 01:42:46 -070059import time
60import uuid
61import httplib
kate15f1c382016-12-15 01:12:40 -080062import hashlib
bhangare0e571a92017-01-12 04:02:23 -080063import socket
64import struct
65import netaddr
bayramov325fa1c2016-09-08 01:42:46 -070066
bayramovbd6160f2016-09-28 04:12:05 +040067# global variable for vcd connector type
68STANDALONE = 'standalone'
69
kate15f1c382016-12-15 01:12:40 -080070# key for flavor dicts
71FLAVOR_RAM_KEY = 'ram'
72FLAVOR_VCPUS_KEY = 'vcpus'
bhangarea92ae392017-01-12 22:30:29 -080073FLAVOR_DISK_KEY = 'disk'
bhangare0e571a92017-01-12 04:02:23 -080074DEFAULT_IP_PROFILE = {'gateway_address':"192.168.1.1",
75 'dhcp_count':50,
76 'subnet_address':"192.168.1.0/24",
77 'dhcp_enabled':True,
78 'dhcp_start_address':"192.168.1.3",
79 'ip_version':"IPv4",
80 'dns_address':"192.168.1.2"
81 }
82# global variable for wait time
kate13ab2c42016-12-23 01:34:24 -080083INTERVAL_TIME = 5
84MAX_WAIT_TIME = 1800
bayramov325fa1c2016-09-08 01:42:46 -070085
bayramovbd6160f2016-09-28 04:12:05 +040086VCAVERSION = '5.9'
87
bhangare0e571a92017-01-12 04:02:23 -080088__author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
bhangarea92ae392017-01-12 22:30:29 -080089__date__ = "$12-Jan-2017 11:09:29$"
bayramovfe3f3c92016-10-04 07:53:41 +040090__version__ = '0.1'
bayramov325fa1c2016-09-08 01:42:46 -070091
bayramovef390722016-09-27 03:34:46 -070092# -1: "Could not be created",
93# 0: "Unresolved",
94# 1: "Resolved",
95# 2: "Deployed",
96# 3: "Suspended",
97# 4: "Powered on",
98# 5: "Waiting for user input",
99# 6: "Unknown state",
100# 7: "Unrecognized state",
101# 8: "Powered off",
102# 9: "Inconsistent state",
103# 10: "Children do not all have the same status",
104# 11: "Upload initiated, OVF descriptor pending",
105# 12: "Upload initiated, copying contents",
106# 13: "Upload initiated , disk contents pending",
107# 14: "Upload has been quarantined",
108# 15: "Upload quarantine period has expired"
109
110# mapping vCD status to MANO
111vcdStatusCode2manoFormat = {4: 'ACTIVE',
112 7: 'PAUSED',
113 3: 'SUSPENDED',
114 8: 'INACTIVE',
115 12: 'BUILD',
116 -1: 'ERROR',
117 14: 'DELETED'}
118
119#
120netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
121 'ERROR': 'ERROR', 'DELETED': 'DELETED'
122 }
123
bayramovbd6160f2016-09-28 04:12:05 +0400124# dict used to store flavor in memory
bayramov325fa1c2016-09-08 01:42:46 -0700125flavorlist = {}
126
bayramovef390722016-09-27 03:34:46 -0700127
bayramovbd6160f2016-09-28 04:12:05 +0400128class vimconnector(vimconn.vimconnector):
129 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
tiernofe789902016-09-29 14:20:44 +0000130 url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}):
bayramovb6ffe792016-09-28 11:50:56 +0400131 """
132 Constructor create vmware connector to vCloud director.
133
134 By default construct doesn't validate connection state. So client can create object with None arguments.
135 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
136
137 a) It initialize organization UUID
138 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
139
140 Args:
141 uuid - is organization uuid.
142 name - is organization name that must be presented in vCloud director.
143 tenant_id - is VDC uuid it must be presented in vCloud director
144 tenant_name - is VDC name.
145 url - is hostname or ip address of vCloud director
146 url_admin - same as above.
147 user - is user that administrator for organization. Caller must make sure that
148 username has right privileges.
149
150 password - is password for a user.
151
152 VMware connector also requires PVDC administrative privileges and separate account.
153 This variables must be passed via config argument dict contains keys
154
155 dict['admin_username']
156 dict['admin_password']
157
158 Returns:
159 Nothing.
160 """
161
bayramovbd6160f2016-09-28 04:12:05 +0400162 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
163 url_admin, user, passwd, log_level, config)
kate15f1c382016-12-15 01:12:40 -0800164
165 self.logger = logging.getLogger('openmano.vim.vmware')
166 self.logger.setLevel(10)
167
bayramovef390722016-09-27 03:34:46 -0700168 self.name = name
kate15f1c382016-12-15 01:12:40 -0800169 self.id = uuid
bayramovef390722016-09-27 03:34:46 -0700170 self.url = url
bayramov325fa1c2016-09-08 01:42:46 -0700171 self.url_admin = url_admin
172 self.tenant_id = tenant_id
173 self.tenant_name = tenant_name
bayramovef390722016-09-27 03:34:46 -0700174 self.user = user
175 self.passwd = passwd
176 self.config = config
177 self.admin_password = None
178 self.admin_user = None
kate15f1c382016-12-15 01:12:40 -0800179 self.org_name = ""
bhangare985a1fd2017-01-31 01:53:21 -0800180 self.nsx_manager = None
181 self.nsx_user = None
182 self.nsx_password = None
bayramovef390722016-09-27 03:34:46 -0700183
kate15f1c382016-12-15 01:12:40 -0800184 if tenant_name is not None:
185 orgnameandtenant = tenant_name.split(":")
186 if len(orgnameandtenant) == 2:
katec324e002016-12-23 00:54:47 -0800187 self.tenant_name = orgnameandtenant[1]
188 self.org_name = orgnameandtenant[0]
kate15f1c382016-12-15 01:12:40 -0800189 else:
190 self.tenant_name = tenant_name
bhangarea92ae392017-01-12 22:30:29 -0800191 if "orgname" in config:
kate15f1c382016-12-15 01:12:40 -0800192 self.org_name = config['orgname']
katec324e002016-12-23 00:54:47 -0800193
tiernofe789902016-09-29 14:20:44 +0000194 if log_level:
bayramov5761ad12016-10-04 09:00:30 +0400195 self.logger.setLevel(getattr(logging, log_level))
bayramov325fa1c2016-09-08 01:42:46 -0700196
bayramovef390722016-09-27 03:34:46 -0700197 try:
198 self.admin_user = config['admin_username']
199 self.admin_password = config['admin_password']
200 except KeyError:
bayramovbd6160f2016-09-28 04:12:05 +0400201 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
bayramov325fa1c2016-09-08 01:42:46 -0700202
bhangare985a1fd2017-01-31 01:53:21 -0800203 try:
204 self.nsx_manager = config['nsx_manager']
205 self.nsx_user = config['nsx_user']
206 self.nsx_password = config['nsx_password']
207 except KeyError:
208 raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
209
bayramovef390722016-09-27 03:34:46 -0700210 self.org_uuid = None
211 self.vca = None
bayramov325fa1c2016-09-08 01:42:46 -0700212
213 if not url:
bayramov5761ad12016-10-04 09:00:30 +0400214 raise vimconn.vimconnException('url param can not be NoneType')
bayramov325fa1c2016-09-08 01:42:46 -0700215
bayramovef390722016-09-27 03:34:46 -0700216 if not self.url_admin: # try to use normal url
bayramov325fa1c2016-09-08 01:42:46 -0700217 self.url_admin = self.url
218
kate15f1c382016-12-15 01:12:40 -0800219 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
bayramovef390722016-09-27 03:34:46 -0700220 self.tenant_id, self.tenant_name))
221 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
222 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
bayramov325fa1c2016-09-08 01:42:46 -0700223
bayramovef390722016-09-27 03:34:46 -0700224 # initialize organization
bayramovbd6160f2016-09-28 04:12:05 +0400225 if self.user is not None and self.passwd is not None and self.url:
226 self.init_organization()
bayramovef390722016-09-27 03:34:46 -0700227
228 def __getitem__(self, index):
kate15f1c382016-12-15 01:12:40 -0800229 if index == 'name':
230 return self.name
bayramovef390722016-09-27 03:34:46 -0700231 if index == 'tenant_id':
bayramov325fa1c2016-09-08 01:42:46 -0700232 return self.tenant_id
bayramovef390722016-09-27 03:34:46 -0700233 if index == 'tenant_name':
bayramov325fa1c2016-09-08 01:42:46 -0700234 return self.tenant_name
bayramovef390722016-09-27 03:34:46 -0700235 elif index == 'id':
bayramov325fa1c2016-09-08 01:42:46 -0700236 return self.id
bayramovef390722016-09-27 03:34:46 -0700237 elif index == 'org_name':
238 return self.org_name
239 elif index == 'org_uuid':
240 return self.org_uuid
241 elif index == 'user':
bayramov325fa1c2016-09-08 01:42:46 -0700242 return self.user
bayramovef390722016-09-27 03:34:46 -0700243 elif index == 'passwd':
bayramov325fa1c2016-09-08 01:42:46 -0700244 return self.passwd
bayramovef390722016-09-27 03:34:46 -0700245 elif index == 'url':
bayramov325fa1c2016-09-08 01:42:46 -0700246 return self.url
bayramovef390722016-09-27 03:34:46 -0700247 elif index == 'url_admin':
bayramov325fa1c2016-09-08 01:42:46 -0700248 return self.url_admin
bayramovef390722016-09-27 03:34:46 -0700249 elif index == "config":
bayramov325fa1c2016-09-08 01:42:46 -0700250 return self.config
251 else:
bayramovef390722016-09-27 03:34:46 -0700252 raise KeyError("Invalid key '%s'" % str(index))
bayramov325fa1c2016-09-08 01:42:46 -0700253
bayramovef390722016-09-27 03:34:46 -0700254 def __setitem__(self, index, value):
kate15f1c382016-12-15 01:12:40 -0800255 if index == 'name':
256 self.name = value
bayramovef390722016-09-27 03:34:46 -0700257 if index == 'tenant_id':
bayramov325fa1c2016-09-08 01:42:46 -0700258 self.tenant_id = value
bayramovef390722016-09-27 03:34:46 -0700259 if index == 'tenant_name':
bayramov325fa1c2016-09-08 01:42:46 -0700260 self.tenant_name = value
bayramovef390722016-09-27 03:34:46 -0700261 elif index == 'id':
bayramov325fa1c2016-09-08 01:42:46 -0700262 self.id = value
bayramovef390722016-09-27 03:34:46 -0700263 elif index == 'org_name':
264 self.org_name = value
bayramovef390722016-09-27 03:34:46 -0700265 elif index == 'org_uuid':
kate15f1c382016-12-15 01:12:40 -0800266 self.org_uuid = value
bayramovef390722016-09-27 03:34:46 -0700267 elif index == 'user':
bayramov325fa1c2016-09-08 01:42:46 -0700268 self.user = value
bayramovef390722016-09-27 03:34:46 -0700269 elif index == 'passwd':
bayramov325fa1c2016-09-08 01:42:46 -0700270 self.passwd = value
bayramovef390722016-09-27 03:34:46 -0700271 elif index == 'url':
bayramov325fa1c2016-09-08 01:42:46 -0700272 self.url = value
bayramovef390722016-09-27 03:34:46 -0700273 elif index == 'url_admin':
bayramov325fa1c2016-09-08 01:42:46 -0700274 self.url_admin = value
275 else:
bayramovef390722016-09-27 03:34:46 -0700276 raise KeyError("Invalid key '%s'" % str(index))
bayramov325fa1c2016-09-08 01:42:46 -0700277
bayramovef390722016-09-27 03:34:46 -0700278 def connect_as_admin(self):
bayramovb6ffe792016-09-28 11:50:56 +0400279 """ Method connect as pvdc admin user to vCloud director.
280 There are certain action that can be done only by provider vdc admin user.
281 Organization creation / provider network creation etc.
bayramovef390722016-09-27 03:34:46 -0700282
283 Returns:
284 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
285 """
286
kate15f1c382016-12-15 01:12:40 -0800287 self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
bayramov325fa1c2016-09-08 01:42:46 -0700288
bayramovef390722016-09-27 03:34:46 -0700289 vca_admin = VCA(host=self.url,
290 username=self.admin_user,
bayramovbd6160f2016-09-28 04:12:05 +0400291 service_type=STANDALONE,
292 version=VCAVERSION,
bayramovef390722016-09-27 03:34:46 -0700293 verify=False,
294 log=False)
295 result = vca_admin.login(password=self.admin_password, org='System')
296 if not result:
bayramovbd6160f2016-09-28 04:12:05 +0400297 raise vimconn.vimconnConnectionException(
298 "Can't connect to a vCloud director as: {}".format(self.admin_user))
bayramovef390722016-09-27 03:34:46 -0700299 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
300 if result is True:
301 self.logger.info(
302 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
bayramov325fa1c2016-09-08 01:42:46 -0700303
bayramovef390722016-09-27 03:34:46 -0700304 return vca_admin
bayramov325fa1c2016-09-08 01:42:46 -0700305
bayramovef390722016-09-27 03:34:46 -0700306 def connect(self):
307 """ Method connect as normal user to vCloud director.
308
309 Returns:
310 The return vca object that letter can be used to connect to vCloud director as admin for VDC
311 """
312
bayramovb6ffe792016-09-28 11:50:56 +0400313 try:
kate15f1c382016-12-15 01:12:40 -0800314 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
315 self.user,
316 self.org_name))
bayramovb6ffe792016-09-28 11:50:56 +0400317 vca = VCA(host=self.url,
318 username=self.user,
319 service_type=STANDALONE,
320 version=VCAVERSION,
321 verify=False,
322 log=False)
bayramovef390722016-09-27 03:34:46 -0700323
kate15f1c382016-12-15 01:12:40 -0800324 result = vca.login(password=self.passwd, org=self.org_name)
bayramovb6ffe792016-09-28 11:50:56 +0400325 if not result:
326 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
kate15f1c382016-12-15 01:12:40 -0800327 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
bayramovb6ffe792016-09-28 11:50:56 +0400328 if result is True:
bayramovfe3f3c92016-10-04 07:53:41 +0400329 self.logger.info(
kate15f1c382016-12-15 01:12:40 -0800330 "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
bayramovb6ffe792016-09-28 11:50:56 +0400331
332 except:
kate15f1c382016-12-15 01:12:40 -0800333 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
334 "{} as user: {}".format(self.org_name, self.user))
bayramov325fa1c2016-09-08 01:42:46 -0700335
336 return vca
337
bayramovbd6160f2016-09-28 04:12:05 +0400338 def init_organization(self):
339 """ Method initialize organization UUID and VDC parameters.
340
341 At bare minimum client must provide organization name that present in vCloud director and VDC.
342
343 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
344 The Org - UUID will be initialized at the run time if data center present in vCloud director.
bayramov325fa1c2016-09-08 01:42:46 -0700345
bayramovef390722016-09-27 03:34:46 -0700346 Returns:
347 The return vca object that letter can be used to connect to vcloud direct as admin
348 """
349 try:
350 if self.org_uuid is None:
351 org_dict = self.get_org_list()
352 for org in org_dict:
bayramovbd6160f2016-09-28 04:12:05 +0400353 # we set org UUID at the init phase but we can do it only when we have valid credential.
bayramovef390722016-09-27 03:34:46 -0700354 if org_dict[org] == self.org_name:
355 self.org_uuid = org
bayramovbd6160f2016-09-28 04:12:05 +0400356 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
357 break
bayramovbd6160f2016-09-28 04:12:05 +0400358 else:
359 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
360
361 # if well good we require for org details
362 org_details_dict = self.get_org(org_uuid=self.org_uuid)
363
364 # we have two case if we want to initialize VDC ID or VDC name at run time
365 # tenant_name provided but no tenant id
bayramov5761ad12016-10-04 09:00:30 +0400366 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
bayramovbd6160f2016-09-28 04:12:05 +0400367 vdcs_dict = org_details_dict['vdcs']
bayramovbd6160f2016-09-28 04:12:05 +0400368 for vdc in vdcs_dict:
369 if vdcs_dict[vdc] == self.tenant_name:
370 self.tenant_id = vdc
371 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
kate15f1c382016-12-15 01:12:40 -0800372 self.org_name))
bayramovbd6160f2016-09-28 04:12:05 +0400373 break
374 else:
375 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
376 # case two we have tenant_id but we don't have tenant name so we find and set it.
bayramov5761ad12016-10-04 09:00:30 +0400377 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
bayramovbd6160f2016-09-28 04:12:05 +0400378 vdcs_dict = org_details_dict['vdcs']
379 for vdc in vdcs_dict:
380 if vdc == self.tenant_id:
381 self.tenant_name = vdcs_dict[vdc]
382 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
kate15f1c382016-12-15 01:12:40 -0800383 self.org_name))
bayramovbd6160f2016-09-28 04:12:05 +0400384 break
385 else:
386 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
bayramovef390722016-09-27 03:34:46 -0700387 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
388 except:
389 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
390 self.logger.debug(traceback.format_exc())
391 self.org_uuid = None
bayramov325fa1c2016-09-08 01:42:46 -0700392
bayramovef390722016-09-27 03:34:46 -0700393 def new_tenant(self, tenant_name=None, tenant_description=None):
bayramovb6ffe792016-09-28 11:50:56 +0400394 """ Method adds a new tenant to VIM with this name.
395 This action requires access to create VDC action in vCloud director.
bayramovef390722016-09-27 03:34:46 -0700396
bayramovb6ffe792016-09-28 11:50:56 +0400397 Args:
398 tenant_name is tenant_name to be created.
399 tenant_description not used for this call
400
401 Return:
402 returns the tenant identifier in UUID format.
403 If action is failed method will throw vimconn.vimconnException method
bayramovbd6160f2016-09-28 04:12:05 +0400404 """
bayramovef390722016-09-27 03:34:46 -0700405 vdc_task = self.create_vdc(vdc_name=tenant_name)
406 if vdc_task is not None:
407 vdc_uuid, value = vdc_task.popitem()
408 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
409 return vdc_uuid
410 else:
bayramovbd6160f2016-09-28 04:12:05 +0400411 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
bayramovef390722016-09-27 03:34:46 -0700412
bayramov163f1ae2016-09-28 17:16:55 +0400413 def delete_tenant(self, tenant_id=None):
bayramovef390722016-09-27 03:34:46 -0700414 """Delete a tenant from VIM"""
415 'Returns the tenant identifier'
bayramovbd6160f2016-09-28 04:12:05 +0400416 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -0700417
418 def get_tenant_list(self, filter_dict={}):
bayramovb6ffe792016-09-28 11:50:56 +0400419 """Obtain tenants of VIM
bayramov325fa1c2016-09-08 01:42:46 -0700420 filter_dict can contain the following keys:
421 name: filter by tenant name
422 id: filter by tenant uuid/id
423 <other VIM specific>
bayramovb6ffe792016-09-28 11:50:56 +0400424 Returns the tenant list of dictionaries:
bayramov325fa1c2016-09-08 01:42:46 -0700425 [{'name':'<name>, 'id':'<id>, ...}, ...]
bayramov325fa1c2016-09-08 01:42:46 -0700426
bayramovb6ffe792016-09-28 11:50:56 +0400427 """
bayramovef390722016-09-27 03:34:46 -0700428 org_dict = self.get_org(self.org_uuid)
429 vdcs_dict = org_dict['vdcs']
430
431 vdclist = []
432 try:
433 for k in vdcs_dict:
434 entry = {'name': vdcs_dict[k], 'id': k}
bayramovb6ffe792016-09-28 11:50:56 +0400435 # if caller didn't specify dictionary we return all tenants.
436 if filter_dict is not None and filter_dict:
437 filtered_entry = entry.copy()
438 filtered_dict = set(entry.keys()) - set(filter_dict)
439 for unwanted_key in filtered_dict: del entry[unwanted_key]
440 if filter_dict == entry:
441 vdclist.append(filtered_entry)
442 else:
443 vdclist.append(entry)
bayramovef390722016-09-27 03:34:46 -0700444 except:
445 self.logger.debug("Error in get_tenant_list()")
446 self.logger.debug(traceback.format_exc())
bayramovb6ffe792016-09-28 11:50:56 +0400447 raise vimconn.vimconnException("Incorrect state. {}")
bayramovef390722016-09-27 03:34:46 -0700448
449 return vdclist
450
451 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
bayramovb6ffe792016-09-28 11:50:56 +0400452 """Adds a tenant network to VIM
bayramov325fa1c2016-09-08 01:42:46 -0700453 net_name is the name
bhangare0e571a92017-01-12 04:02:23 -0800454 net_type can be 'bridge','data'.'ptp'.
bayramovb6ffe792016-09-28 11:50:56 +0400455 ip_profile is a dict containing the IP parameters of the network
bayramov325fa1c2016-09-08 01:42:46 -0700456 shared is a boolean
bayramovb6ffe792016-09-28 11:50:56 +0400457 Returns the network identifier"""
bayramov325fa1c2016-09-08 01:42:46 -0700458
bhangare0e571a92017-01-12 04:02:23 -0800459 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
460 .format(net_name, net_type, ip_profile, shared))
bayramov325fa1c2016-09-08 01:42:46 -0700461
bayramovef390722016-09-27 03:34:46 -0700462 isshared = 'false'
463 if shared:
464 isshared = 'true'
465
bhangare0e571a92017-01-12 04:02:23 -0800466 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
467 ip_profile=ip_profile, isshared=isshared)
bayramovef390722016-09-27 03:34:46 -0700468 if network_uuid is not None:
469 return network_uuid
470 else:
bayramovbd6160f2016-09-28 04:12:05 +0400471 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
bayramovef390722016-09-27 03:34:46 -0700472
473 def get_vcd_network_list(self):
474 """ Method available organization for a logged in tenant
475
476 Returns:
477 The return vca object that letter can be used to connect to vcloud direct as admin
478 """
479
kate15f1c382016-12-15 01:12:40 -0800480 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
bayramovef390722016-09-27 03:34:46 -0700481 vca = self.connect()
482 if not vca:
483 raise vimconn.vimconnConnectionException("self.connect() is failed.")
484
kate15f1c382016-12-15 01:12:40 -0800485 if not self.tenant_name:
486 raise vimconn.vimconnConnectionException("Tenant name is empty.")
487
bayramovef390722016-09-27 03:34:46 -0700488 vdc = vca.get_vdc(self.tenant_name)
kate15f1c382016-12-15 01:12:40 -0800489 if vdc is None:
490 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
491
bayramovef390722016-09-27 03:34:46 -0700492 vdc_uuid = vdc.get_id().split(":")[3]
493 networks = vca.get_networks(vdc.get_name())
494 network_list = []
495 try:
496 for network in networks:
497 filter_dict = {}
498 netid = network.get_id().split(":")
499 if len(netid) != 4:
500 continue
501
502 filter_dict["name"] = network.get_name()
503 filter_dict["id"] = netid[3]
504 filter_dict["shared"] = network.get_IsShared()
505 filter_dict["tenant_id"] = vdc_uuid
506 if network.get_status() == 1:
507 filter_dict["admin_state_up"] = True
508 else:
509 filter_dict["admin_state_up"] = False
510 filter_dict["status"] = "ACTIVE"
511 filter_dict["type"] = "bridge"
512 network_list.append(filter_dict)
513 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
514 except:
515 self.logger.debug("Error in get_vcd_network_list")
516 self.logger.debug(traceback.format_exc())
517 pass
518
519 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
520 return network_list
bayramov325fa1c2016-09-08 01:42:46 -0700521
522 def get_network_list(self, filter_dict={}):
bayramovb6ffe792016-09-28 11:50:56 +0400523 """Obtain tenant networks of VIM
bayramov325fa1c2016-09-08 01:42:46 -0700524 Filter_dict can be:
bayramovef390722016-09-27 03:34:46 -0700525 name: network name OR/AND
526 id: network uuid OR/AND
527 shared: boolean OR/AND
528 tenant_id: tenant OR/AND
bayramov325fa1c2016-09-08 01:42:46 -0700529 admin_state_up: boolean
530 status: 'ACTIVE'
bayramovef390722016-09-27 03:34:46 -0700531
532 [{key : value , key : value}]
533
bayramov325fa1c2016-09-08 01:42:46 -0700534 Returns the network list of dictionaries:
535 [{<the fields at Filter_dict plus some VIM specific>}, ...]
536 List can be empty
bayramovb6ffe792016-09-28 11:50:56 +0400537 """
bayramov325fa1c2016-09-08 01:42:46 -0700538
kate15f1c382016-12-15 01:12:40 -0800539 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
bayramov325fa1c2016-09-08 01:42:46 -0700540 vca = self.connect()
541 if not vca:
kate15f1c382016-12-15 01:12:40 -0800542 raise vimconn.vimconnConnectionException("self.connect() is failed.")
543
544 if not self.tenant_name:
545 raise vimconn.vimconnConnectionException("Tenant name is empty.")
bayramov325fa1c2016-09-08 01:42:46 -0700546
547 vdc = vca.get_vdc(self.tenant_name)
kate15f1c382016-12-15 01:12:40 -0800548 if vdc is None:
549 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
bayramov325fa1c2016-09-08 01:42:46 -0700550
kate15f1c382016-12-15 01:12:40 -0800551 vdcid = vdc.get_id().split(":")[3]
bayramov325fa1c2016-09-08 01:42:46 -0700552 networks = vca.get_networks(vdc.get_name())
553 network_list = []
bayramov325fa1c2016-09-08 01:42:46 -0700554
bayramovef390722016-09-27 03:34:46 -0700555 try:
556 for network in networks:
557 filter_entry = {}
558 net_uuid = network.get_id().split(":")
559 if len(net_uuid) != 4:
560 continue
561 else:
562 net_uuid = net_uuid[3]
563 # create dict entry
564 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
565 vdcid,
566 network.get_name()))
567 filter_entry["name"] = network.get_name()
568 filter_entry["id"] = net_uuid
569 filter_entry["shared"] = network.get_IsShared()
570 filter_entry["tenant_id"] = vdcid
571 if network.get_status() == 1:
572 filter_entry["admin_state_up"] = True
573 else:
574 filter_entry["admin_state_up"] = False
575 filter_entry["status"] = "ACTIVE"
576 filter_entry["type"] = "bridge"
577 filtered_entry = filter_entry.copy()
578
bayramovb6ffe792016-09-28 11:50:56 +0400579 if filter_dict is not None and filter_dict:
580 # we remove all the key : value we don't care and match only
581 # respected field
582 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
583 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
584 if filter_dict == filter_entry:
585 network_list.append(filtered_entry)
586 else:
bayramovef390722016-09-27 03:34:46 -0700587 network_list.append(filtered_entry)
588 except:
589 self.logger.debug("Error in get_vcd_network_list")
590 self.logger.debug(traceback.format_exc())
bayramov325fa1c2016-09-08 01:42:46 -0700591
592 self.logger.debug("Returning {}".format(network_list))
593 return network_list
594
595 def get_network(self, net_id):
bayramovfe3f3c92016-10-04 07:53:41 +0400596 """Method obtains network details of net_id VIM network
bayramovef390722016-09-27 03:34:46 -0700597 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
598
599 vca = self.connect()
600 if not vca:
601 raise vimconn.vimconnConnectionException("self.connect() is failed")
602
603 vdc = vca.get_vdc(self.tenant_name)
604 vdc_id = vdc.get_id().split(":")[3]
605
606 networks = vca.get_networks(vdc.get_name())
607 filter_dict = {}
608
609 try:
610 for network in networks:
611 vdc_network_id = network.get_id().split(":")
612 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
613 filter_dict["name"] = network.get_name()
614 filter_dict["id"] = vdc_network_id[3]
615 filter_dict["shared"] = network.get_IsShared()
616 filter_dict["tenant_id"] = vdc_id
617 if network.get_status() == 1:
618 filter_dict["admin_state_up"] = True
619 else:
620 filter_dict["admin_state_up"] = False
621 filter_dict["status"] = "ACTIVE"
622 filter_dict["type"] = "bridge"
623 self.logger.debug("Returning {}".format(filter_dict))
624 return filter_dict
625 except:
626 self.logger.debug("Error in get_network")
627 self.logger.debug(traceback.format_exc())
628
629 return filter_dict
bayramov325fa1c2016-09-08 01:42:46 -0700630
631 def delete_network(self, net_id):
bayramovef390722016-09-27 03:34:46 -0700632 """
633 Method Deletes a tenant network from VIM, provide the network id.
634
635 Returns the network identifier or raise an exception
636 """
637
638 vca = self.connect()
639 if not vca:
bayramovfe3f3c92016-10-04 07:53:41 +0400640 raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
bayramovef390722016-09-27 03:34:46 -0700641
bayramovfe3f3c92016-10-04 07:53:41 +0400642 vcd_network = self.get_vcd_network(network_uuid=net_id)
643 if vcd_network is not None and vcd_network:
644 if self.delete_network_action(network_uuid=net_id):
645 return net_id
bayramovef390722016-09-27 03:34:46 -0700646 else:
647 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
bayramov325fa1c2016-09-08 01:42:46 -0700648
649 def refresh_nets_status(self, net_list):
bayramovbd6160f2016-09-28 04:12:05 +0400650 """Get the status of the networks
bayramov325fa1c2016-09-08 01:42:46 -0700651 Params: the list of network identifiers
652 Returns a dictionary with:
653 net_id: #VIM id of this network
654 status: #Mandatory. Text with one of:
655 # DELETED (not found at vim)
bayramovbd6160f2016-09-28 04:12:05 +0400656 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
bayramov325fa1c2016-09-08 01:42:46 -0700657 # OTHER (Vim reported other status not understood)
658 # ERROR (VIM indicates an ERROR status)
bayramovbd6160f2016-09-28 04:12:05 +0400659 # ACTIVE, INACTIVE, DOWN (admin down),
bayramov325fa1c2016-09-08 01:42:46 -0700660 # BUILD (on building process)
661 #
bayramovbd6160f2016-09-28 04:12:05 +0400662 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
bayramov325fa1c2016-09-08 01:42:46 -0700663 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
664
bayramovbd6160f2016-09-28 04:12:05 +0400665 """
bayramov325fa1c2016-09-08 01:42:46 -0700666
bayramov325fa1c2016-09-08 01:42:46 -0700667 vca = self.connect()
668 if not vca:
bayramovef390722016-09-27 03:34:46 -0700669 raise vimconn.vimconnConnectionException("self.connect() is failed")
670
671 dict_entry = {}
672 try:
673 for net in net_list:
bayramovef390722016-09-27 03:34:46 -0700674 errormsg = ''
675 vcd_network = self.get_vcd_network(network_uuid=net)
bayramovfe3f3c92016-10-04 07:53:41 +0400676 if vcd_network is not None and vcd_network:
bhangare92d4af32016-12-24 02:54:51 -0800677 if vcd_network['status'] == '1':
bayramovef390722016-09-27 03:34:46 -0700678 status = 'ACTIVE'
679 else:
680 status = 'DOWN'
681 else:
682 status = 'DELETED'
bayramovfe3f3c92016-10-04 07:53:41 +0400683 errormsg = 'Network not found.'
684
685 dict_entry[net] = {'status': status, 'error_msg': errormsg,
bhangare92d4af32016-12-24 02:54:51 -0800686 'vim_info': yaml.safe_dump(vcd_network)}
bayramovef390722016-09-27 03:34:46 -0700687 except:
688 self.logger.debug("Error in refresh_nets_status")
689 self.logger.debug(traceback.format_exc())
690
691 return dict_entry
692
bayramovbd6160f2016-09-28 04:12:05 +0400693 def get_flavor(self, flavor_id):
bayramovef390722016-09-27 03:34:46 -0700694 """Obtain flavor details from the VIM
695 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
696 """
bayramov5761ad12016-10-04 09:00:30 +0400697 if flavor_id not in flavorlist:
bayramovfe3f3c92016-10-04 07:53:41 +0400698 raise vimconn.vimconnNotFoundException("Flavor not found.")
bayramovef390722016-09-27 03:34:46 -0700699 return flavorlist[flavor_id]
bayramov325fa1c2016-09-08 01:42:46 -0700700
701 def new_flavor(self, flavor_data):
bayramovef390722016-09-27 03:34:46 -0700702 """Adds a tenant flavor to VIM
bayramov325fa1c2016-09-08 01:42:46 -0700703 flavor_data contains a dictionary with information, keys:
704 name: flavor name
705 ram: memory (cloud type) in MBytes
706 vpcus: cpus (cloud type)
707 extended: EPA parameters
708 - numas: #items requested in same NUMA
709 memory: number of 1G huge pages memory
710 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
711 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
712 - name: interface name
713 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
714 bandwidth: X Gbps; requested guarantee bandwidth
bayramovef390722016-09-27 03:34:46 -0700715 vpci: requested virtual PCI address
bayramov325fa1c2016-09-08 01:42:46 -0700716 disk: disk size
717 is_public:
bayramov325fa1c2016-09-08 01:42:46 -0700718 #TODO to concrete
bayramovef390722016-09-27 03:34:46 -0700719 Returns the flavor identifier"""
bayramov325fa1c2016-09-08 01:42:46 -0700720
bayramovef390722016-09-27 03:34:46 -0700721 # generate a new uuid put to internal dict and return it.
bhangarea92ae392017-01-12 22:30:29 -0800722 self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
723 new_flavor=flavor_data
724 ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
725 cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
726 disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
727
728 extended_flv = flavor_data.get("extended")
729 if extended_flv:
730 numas=extended_flv.get("numas")
731 if numas:
732 for numa in numas:
733 #overwrite ram and vcpus
734 ram = numa['memory']*1024
735 if 'paired-threads' in numa:
736 cpu = numa['paired-threads']*2
737 elif 'cores' in numa:
738 cpu = numa['cores']
739 elif 'threads' in numa:
740 cpu = numa['threads']
741
742 new_flavor[FLAVOR_RAM_KEY] = ram
743 new_flavor[FLAVOR_VCPUS_KEY] = cpu
744 new_flavor[FLAVOR_DISK_KEY] = disk
745 # generate a new uuid put to internal dict and return it.
bayramovef390722016-09-27 03:34:46 -0700746 flavor_id = uuid.uuid4()
bhangarea92ae392017-01-12 22:30:29 -0800747 flavorlist[str(flavor_id)] = new_flavor
748 self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
bayramov325fa1c2016-09-08 01:42:46 -0700749
bayramovef390722016-09-27 03:34:46 -0700750 return str(flavor_id)
bayramov325fa1c2016-09-08 01:42:46 -0700751
752 def delete_flavor(self, flavor_id):
bayramovef390722016-09-27 03:34:46 -0700753 """Deletes a tenant flavor from VIM identify by its id
bayramov325fa1c2016-09-08 01:42:46 -0700754
bayramovfe3f3c92016-10-04 07:53:41 +0400755 Returns the used id or raise an exception
bayramovef390722016-09-27 03:34:46 -0700756 """
bayramov5761ad12016-10-04 09:00:30 +0400757 if flavor_id not in flavorlist:
bayramovfe3f3c92016-10-04 07:53:41 +0400758 raise vimconn.vimconnNotFoundException("Flavor not found.")
bayramovef390722016-09-27 03:34:46 -0700759
bayramovef390722016-09-27 03:34:46 -0700760 flavorlist.pop(flavor_id, None)
761 return flavor_id
762
763 def new_image(self, image_dict):
bayramov5761ad12016-10-04 09:00:30 +0400764 """
bayramov325fa1c2016-09-08 01:42:46 -0700765 Adds a tenant image to VIM
766 Returns:
767 200, image-id if the image is created
768 <0, message if there is an error
bayramov5761ad12016-10-04 09:00:30 +0400769 """
bayramov325fa1c2016-09-08 01:42:46 -0700770
bayramovef390722016-09-27 03:34:46 -0700771 return self.get_image_id_from_path(image_dict['location'])
bayramov325fa1c2016-09-08 01:42:46 -0700772
773 def delete_image(self, image_id):
bayramovfe3f3c92016-10-04 07:53:41 +0400774 """
775
776 :param image_id:
777 :return:
778 """
bayramovfe3f3c92016-10-04 07:53:41 +0400779
bayramovbd6160f2016-09-28 04:12:05 +0400780 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -0700781
782 def catalog_exists(self, catalog_name, catalogs):
bayramovfe3f3c92016-10-04 07:53:41 +0400783 """
784
785 :param catalog_name:
786 :param catalogs:
787 :return:
788 """
bayramov325fa1c2016-09-08 01:42:46 -0700789 for catalog in catalogs:
790 if catalog.name == catalog_name:
791 return True
792 return False
793
bayramovb6ffe792016-09-28 11:50:56 +0400794 def create_vimcatalog(self, vca=None, catalog_name=None):
bayramovfe3f3c92016-10-04 07:53:41 +0400795 """ Create new catalog entry in vCloud director.
bayramovb6ffe792016-09-28 11:50:56 +0400796
797 Args
798 vca: vCloud director.
799 catalog_name catalog that client wish to create. Note no validation done for a name.
800 Client must make sure that provide valid string representation.
801
802 Return (bool) True if catalog created.
803
804 """
805 try:
806 task = vca.create_catalog(catalog_name, catalog_name)
807 result = vca.block_until_completed(task)
808 if not result:
809 return False
810 catalogs = vca.get_catalogs()
811 except:
bayramov325fa1c2016-09-08 01:42:46 -0700812 return False
bayramov325fa1c2016-09-08 01:42:46 -0700813 return self.catalog_exists(catalog_name, catalogs)
814
bayramov5761ad12016-10-04 09:00:30 +0400815 # noinspection PyIncorrectDocstring
bayramovfe3f3c92016-10-04 07:53:41 +0400816 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
817 description='', progress=False, chunk_bytes=128 * 1024):
bayramov325fa1c2016-09-08 01:42:46 -0700818 """
819 Uploads a OVF file to a vCloud catalog
820
bayramov5761ad12016-10-04 09:00:30 +0400821 :param chunk_bytes:
822 :param progress:
823 :param description:
824 :param image_name:
825 :param vca:
bayramov325fa1c2016-09-08 01:42:46 -0700826 :param catalog_name: (str): The name of the catalog to upload the media.
bayramov325fa1c2016-09-08 01:42:46 -0700827 :param media_file_name: (str): The name of the local media file to upload.
828 :return: (bool) True if the media file was successfully uploaded, false otherwise.
829 """
830 os.path.isfile(media_file_name)
831 statinfo = os.stat(media_file_name)
bayramov325fa1c2016-09-08 01:42:46 -0700832
833 # find a catalog entry where we upload OVF.
834 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
835 # status change.
836 # if VCD can parse OVF we upload VMDK file
837 for catalog in vca.get_catalogs():
838 if catalog_name != catalog.name:
839 continue
840 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
841 link.get_rel() == 'add', catalog.get_Link())
842 assert len(link) == 1
843 data = """
kate15f1c382016-12-15 01:12:40 -0800844 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
845 """ % (escape(catalog_name), escape(description))
bayramov325fa1c2016-09-08 01:42:46 -0700846 headers = vca.vcloud_session.get_vcloud_headers()
847 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
848 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
849 if response.status_code == requests.codes.created:
bayramovef390722016-09-27 03:34:46 -0700850 catalogItem = XmlElementTree.fromstring(response.content)
bayramov325fa1c2016-09-08 01:42:46 -0700851 entity = [child for child in catalogItem if
852 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
853 href = entity.get('href')
854 template = href
855 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
856 verify=vca.verify, logger=self.logger)
857
858 if response.status_code == requests.codes.ok:
859 media = mediaType.parseString(response.content, True)
bayramovfe3f3c92016-10-04 07:53:41 +0400860 link = filter(lambda link: link.get_rel() == 'upload:default',
861 media.get_Files().get_File()[0].get_Link())[0]
bayramov325fa1c2016-09-08 01:42:46 -0700862 headers = vca.vcloud_session.get_vcloud_headers()
863 headers['Content-Type'] = 'Content-Type text/xml'
bayramovfe3f3c92016-10-04 07:53:41 +0400864 response = Http.put(link.get_href(),
865 data=open(media_file_name, 'rb'),
866 headers=headers,
bayramovef390722016-09-27 03:34:46 -0700867 verify=vca.verify, logger=self.logger)
bayramov325fa1c2016-09-08 01:42:46 -0700868 if response.status_code != requests.codes.ok:
bayramovef390722016-09-27 03:34:46 -0700869 self.logger.debug(
870 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
871 media_file_name))
bayramov325fa1c2016-09-08 01:42:46 -0700872 return False
873
bayramovef390722016-09-27 03:34:46 -0700874 # TODO fix this with aync block
bayramov325fa1c2016-09-08 01:42:46 -0700875 time.sleep(5)
876
kate15f1c382016-12-15 01:12:40 -0800877 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
bayramov325fa1c2016-09-08 01:42:46 -0700878
879 # uploading VMDK file
bayramovfe3f3c92016-10-04 07:53:41 +0400880 # check status of OVF upload and upload remaining files.
881 response = Http.get(template,
882 headers=vca.vcloud_session.get_vcloud_headers(),
883 verify=vca.verify,
bayramovef390722016-09-27 03:34:46 -0700884 logger=self.logger)
bayramovfe3f3c92016-10-04 07:53:41 +0400885
bayramov325fa1c2016-09-08 01:42:46 -0700886 if response.status_code == requests.codes.ok:
887 media = mediaType.parseString(response.content, True)
bayramovfe3f3c92016-10-04 07:53:41 +0400888 number_of_files = len(media.get_Files().get_File())
889 for index in xrange(0, number_of_files):
890 links_list = filter(lambda link: link.get_rel() == 'upload:default',
891 media.get_Files().get_File()[index].get_Link())
892 for link in links_list:
893 # we skip ovf since it already uploaded.
894 if 'ovf' in link.get_href():
895 continue
896 # The OVF file and VMDK must be in a same directory
897 head, tail = os.path.split(media_file_name)
898 file_vmdk = head + '/' + link.get_href().split("/")[-1]
899 if not os.path.isfile(file_vmdk):
900 return False
901 statinfo = os.stat(file_vmdk)
902 if statinfo.st_size == 0:
903 return False
904 hrefvmdk = link.get_href()
bayramov325fa1c2016-09-08 01:42:46 -0700905
bayramovfe3f3c92016-10-04 07:53:41 +0400906 if progress:
907 print("Uploading file: {}".format(file_vmdk))
908 if progress:
909 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
910 FileTransferSpeed()]
911 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
bayramov325fa1c2016-09-08 01:42:46 -0700912
bayramovfe3f3c92016-10-04 07:53:41 +0400913 bytes_transferred = 0
914 f = open(file_vmdk, 'rb')
915 while bytes_transferred < statinfo.st_size:
916 my_bytes = f.read(chunk_bytes)
917 if len(my_bytes) <= chunk_bytes:
918 headers = vca.vcloud_session.get_vcloud_headers()
919 headers['Content-Range'] = 'bytes %s-%s/%s' % (
920 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
921 headers['Content-Length'] = str(len(my_bytes))
922 response = Http.put(hrefvmdk,
923 headers=headers,
924 data=my_bytes,
925 verify=vca.verify,
926 logger=None)
bayramov325fa1c2016-09-08 01:42:46 -0700927
bayramovfe3f3c92016-10-04 07:53:41 +0400928 if response.status_code == requests.codes.ok:
929 bytes_transferred += len(my_bytes)
930 if progress:
931 progress_bar.update(bytes_transferred)
932 else:
933 self.logger.debug(
934 'file upload failed with error: [%s] %s' % (response.status_code,
935 response.content))
936
937 f.close()
938 return False
939 f.close()
940 if progress:
941 progress_bar.finish()
bhangarea92ae392017-01-12 22:30:29 -0800942 time.sleep(10)
bayramov325fa1c2016-09-08 01:42:46 -0700943 return True
944 else:
945 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
946 format(catalog_name, media_file_name))
947 return False
948
949 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
950 return False
951
bayramovfe3f3c92016-10-04 07:53:41 +0400952 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
bayramov325fa1c2016-09-08 01:42:46 -0700953 """Upload media file"""
bayramovfe3f3c92016-10-04 07:53:41 +0400954 # TODO add named parameters for readability
955
956 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
957 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
bayramov325fa1c2016-09-08 01:42:46 -0700958
bayramovb6ffe792016-09-28 11:50:56 +0400959 def validate_uuid4(self, uuid_string=None):
960 """ Method validate correct format of UUID.
961
962 Return: true if string represent valid uuid
963 """
964 try:
965 val = uuid.UUID(uuid_string, version=4)
966 except ValueError:
967 return False
968 return True
969
970 def get_catalogid(self, catalog_name=None, catalogs=None):
971 """ Method check catalog and return catalog ID in UUID format.
972
973 Args
974 catalog_name: catalog name as string
975 catalogs: list of catalogs.
976
977 Return: catalogs uuid
978 """
979
bayramov325fa1c2016-09-08 01:42:46 -0700980 for catalog in catalogs:
981 if catalog.name == catalog_name:
bayramov325fa1c2016-09-08 01:42:46 -0700982 catalog_id = catalog.get_id().split(":")
983 return catalog_id[3]
984 return None
985
bayramovb6ffe792016-09-28 11:50:56 +0400986 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
987 """ Method check catalog and return catalog name lookup done by catalog UUID.
988
989 Args
990 catalog_name: catalog name as string
991 catalogs: list of catalogs.
992
993 Return: catalogs name or None
994 """
995
996 if not self.validate_uuid4(uuid_string=catalog_uuid):
997 return None
998
bayramov325fa1c2016-09-08 01:42:46 -0700999 for catalog in catalogs:
bayramovb6ffe792016-09-28 11:50:56 +04001000 catalog_id = catalog.get_id().split(":")[3]
1001 if catalog_id == catalog_uuid:
bayramov325fa1c2016-09-08 01:42:46 -07001002 return catalog.name
1003 return None
1004
bayramovfe3f3c92016-10-04 07:53:41 +04001005 def get_image_id_from_path(self, path=None, progress=False):
bayramovb6ffe792016-09-28 11:50:56 +04001006 """ Method upload OVF image to vCloud director.
bayramov325fa1c2016-09-08 01:42:46 -07001007
bayramovb6ffe792016-09-28 11:50:56 +04001008 Each OVF image represented as single catalog entry in vcloud director.
1009 The method check for existing catalog entry. The check done by file name without file extension.
1010
1011 if given catalog name already present method will respond with existing catalog uuid otherwise
1012 it will create new catalog entry and upload OVF file to newly created catalog.
1013
1014 If method can't create catalog entry or upload a file it will throw exception.
1015
bayramovfe3f3c92016-10-04 07:53:41 +04001016 Method accept boolean flag progress that will output progress bar. It useful method
1017 for standalone upload use case. In case to test large file upload.
1018
bayramovb6ffe792016-09-28 11:50:56 +04001019 Args
bayramovfe3f3c92016-10-04 07:53:41 +04001020 path: - valid path to OVF file.
1021 progress - boolean progress bar show progress bar.
bayramovb6ffe792016-09-28 11:50:56 +04001022
1023 Return: if image uploaded correct method will provide image catalog UUID.
1024 """
bayramov325fa1c2016-09-08 01:42:46 -07001025 vca = self.connect()
1026 if not vca:
bayramovfe3f3c92016-10-04 07:53:41 +04001027 raise vimconn.vimconnConnectionException("self.connect() is failed.")
bayramov325fa1c2016-09-08 01:42:46 -07001028
kate15f1c382016-12-15 01:12:40 -08001029 if not path:
bayramovfe3f3c92016-10-04 07:53:41 +04001030 raise vimconn.vimconnException("Image path can't be None.")
1031
1032 if not os.path.isfile(path):
1033 raise vimconn.vimconnException("Can't read file. File not found.")
1034
1035 if not os.access(path, os.R_OK):
1036 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1037
1038 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
bayramov325fa1c2016-09-08 01:42:46 -07001039
1040 dirpath, filename = os.path.split(path)
1041 flname, file_extension = os.path.splitext(path)
1042 if file_extension != '.ovf':
bayramovfe3f3c92016-10-04 07:53:41 +04001043 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
bayramovb6ffe792016-09-28 11:50:56 +04001044 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
kate15f1c382016-12-15 01:12:40 -08001045
bayramov325fa1c2016-09-08 01:42:46 -07001046 catalog_name = os.path.splitext(filename)[0]
kate15f1c382016-12-15 01:12:40 -08001047 catalog_md5_name = hashlib.md5(path).hexdigest()
1048 self.logger.debug("File name {} Catalog Name {} file path {} "
1049 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
bayramov325fa1c2016-09-08 01:42:46 -07001050
1051 catalogs = vca.get_catalogs()
1052 if len(catalogs) == 0:
bayramovfe3f3c92016-10-04 07:53:41 +04001053 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
kate15f1c382016-12-15 01:12:40 -08001054 result = self.create_vimcatalog(vca, catalog_md5_name)
bayramov325fa1c2016-09-08 01:42:46 -07001055 if not result:
kate15f1c382016-12-15 01:12:40 -08001056 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1057 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
bayramovfe3f3c92016-10-04 07:53:41 +04001058 media_name=filename, medial_file_name=path, progress=progress)
bayramov325fa1c2016-09-08 01:42:46 -07001059 if not result:
bayramovb6ffe792016-09-28 11:50:56 +04001060 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
bayramov325fa1c2016-09-08 01:42:46 -07001061 return self.get_catalogid(catalog_name, vca.get_catalogs())
1062 else:
1063 for catalog in catalogs:
1064 # search for existing catalog if we find same name we return ID
1065 # TODO optimize this
kate15f1c382016-12-15 01:12:40 -08001066 if catalog.name == catalog_md5_name:
1067 self.logger.debug("Found existing catalog entry for {} "
1068 "catalog id {}".format(catalog_name,
1069 self.get_catalogid(catalog_md5_name, catalogs)))
1070 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
bayramov325fa1c2016-09-08 01:42:46 -07001071
bayramovfe3f3c92016-10-04 07:53:41 +04001072 # if we didn't find existing catalog we create a new one and upload image.
kate15f1c382016-12-15 01:12:40 -08001073 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1074 result = self.create_vimcatalog(vca, catalog_md5_name)
bayramov325fa1c2016-09-08 01:42:46 -07001075 if not result:
kate15f1c382016-12-15 01:12:40 -08001076 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
bayramovfe3f3c92016-10-04 07:53:41 +04001077
kate15f1c382016-12-15 01:12:40 -08001078 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
bayramovfe3f3c92016-10-04 07:53:41 +04001079 media_name=filename, medial_file_name=path, progress=progress)
bayramov325fa1c2016-09-08 01:42:46 -07001080 if not result:
kate15f1c382016-12-15 01:12:40 -08001081 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
bayramov325fa1c2016-09-08 01:42:46 -07001082
kate15f1c382016-12-15 01:12:40 -08001083 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
bayramov325fa1c2016-09-08 01:42:46 -07001084
kate8fc61fc2017-01-23 19:57:06 -08001085 def get_image_list(self, filter_dict={}):
1086 '''Obtain tenant images from VIM
1087 Filter_dict can be:
1088 name: image name
1089 id: image uuid
1090 checksum: image checksum
1091 location: image path
1092 Returns the image list of dictionaries:
1093 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1094 List can be empty
1095 '''
1096 vca = self.connect()
1097 if not vca:
1098 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1099 try:
1100 image_list = []
1101 catalogs = vca.get_catalogs()
1102 if len(catalogs) == 0:
1103 return image_list
1104 else:
1105 for catalog in catalogs:
1106 catalog_uuid = catalog.get_id().split(":")[3]
1107 name = catalog.name
1108 filtered_dict = {}
kate34718682017-01-24 03:20:43 -08001109 if filter_dict.get("name") and filter_dict["name"] != name:
1110 continue
1111 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1112 continue
1113 filtered_dict ["name"] = name
1114 filtered_dict ["id"] = catalog_uuid
1115 image_list.append(filtered_dict)
kate8fc61fc2017-01-23 19:57:06 -08001116
1117 self.logger.debug("List of already created catalog items: {}".format(image_list))
1118 return image_list
1119 except Exception as exp:
kated63062f2017-01-24 07:26:52 -08001120 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
kate8fc61fc2017-01-23 19:57:06 -08001121
bayramovef390722016-09-27 03:34:46 -07001122 def get_vappid(self, vdc=None, vapp_name=None):
1123 """ Method takes vdc object and vApp name and returns vapp uuid or None
1124
1125 Args:
bayramovef390722016-09-27 03:34:46 -07001126 vdc: The VDC object.
1127 vapp_name: is application vappp name identifier
1128
bayramovb6ffe792016-09-28 11:50:56 +04001129 Returns:
bayramovef390722016-09-27 03:34:46 -07001130 The return vApp name otherwise None
1131 """
bayramovef390722016-09-27 03:34:46 -07001132 if vdc is None or vapp_name is None:
1133 return None
1134 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
bayramov325fa1c2016-09-08 01:42:46 -07001135 try:
1136 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
bayramovef390722016-09-27 03:34:46 -07001137 vdc.ResourceEntities.ResourceEntity)
bayramov325fa1c2016-09-08 01:42:46 -07001138 if len(refs) == 1:
1139 return refs[0].href.split("vapp")[1][1:]
bayramovef390722016-09-27 03:34:46 -07001140 except Exception as e:
1141 self.logger.exception(e)
1142 return False
1143 return None
1144
bayramovfe3f3c92016-10-04 07:53:41 +04001145 def check_vapp(self, vdc=None, vapp_uuid=None):
1146 """ Method Method returns True or False if vapp deployed in vCloud director
bayramovef390722016-09-27 03:34:46 -07001147
1148 Args:
1149 vca: Connector to VCA
1150 vdc: The VDC object.
1151 vappid: vappid is application identifier
1152
1153 Returns:
bayramovfe3f3c92016-10-04 07:53:41 +04001154 The return True if vApp deployed
bayramov5761ad12016-10-04 09:00:30 +04001155 :param vdc:
1156 :param vapp_uuid:
bayramovef390722016-09-27 03:34:46 -07001157 """
1158 try:
1159 refs = filter(lambda ref:
1160 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1161 vdc.ResourceEntities.ResourceEntity)
1162 for ref in refs:
1163 vappid = ref.href.split("vapp")[1][1:]
1164 # find vapp with respected vapp uuid
bayramovfe3f3c92016-10-04 07:53:41 +04001165 if vappid == vapp_uuid:
bayramovef390722016-09-27 03:34:46 -07001166 return True
1167 except Exception as e:
1168 self.logger.exception(e)
1169 return False
1170 return False
1171
bayramovfe3f3c92016-10-04 07:53:41 +04001172 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
bayramovef390722016-09-27 03:34:46 -07001173 """Method returns vApp name from vCD and lookup done by vapp_id.
1174
1175 Args:
1176 vca: Connector to VCA
1177 vdc: The VDC object.
bayramovfe3f3c92016-10-04 07:53:41 +04001178 vapp_uuid: vappid is application identifier
bayramovef390722016-09-27 03:34:46 -07001179
1180 Returns:
1181 The return vApp name otherwise None
1182 """
1183
1184 try:
1185 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1186 vdc.ResourceEntities.ResourceEntity)
1187 for ref in refs:
1188 # we care only about UUID the rest doesn't matter
1189 vappid = ref.href.split("vapp")[1][1:]
bayramovfe3f3c92016-10-04 07:53:41 +04001190 if vappid == vapp_uuid:
bayramovef390722016-09-27 03:34:46 -07001191 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1192 logger=self.logger)
1193 tree = XmlElementTree.fromstring(response.content)
1194 return tree.attrib['name']
1195 except Exception as e:
1196 self.logger.exception(e)
bayramov325fa1c2016-09-08 01:42:46 -07001197 return None
1198 return None
1199
bayramovfe3f3c92016-10-04 07:53:41 +04001200 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
montesmoreno0c8def02016-12-22 12:16:23 +00001201 cloud_config=None, disk_list=None):
bayramov325fa1c2016-09-08 01:42:46 -07001202 """Adds a VM instance to VIM
1203 Params:
1204 start: indicates if VM must start or boot in pause mode. Ignored
1205 image_id,flavor_id: image and flavor uuid
1206 net_list: list of interfaces, each one is a dictionary with:
1207 name:
1208 net_id: network uuid to connect
1209 vpci: virtual vcpi to assign
1210 model: interface model, virtio, e2000, ...
1211 mac_address:
1212 use: 'data', 'bridge', 'mgmt'
1213 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1214 vim_id: filled/added by this function
1215 cloud_config: can be a text script to be passed directly to cloud-init,
1216 or an object to inject users and ssh keys with format:
1217 key-pairs: [] list of keys to install to the default user
1218 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1219 #TODO ip, security groups
1220 Returns >=0, the instance identifier
1221 <0, error_text
1222 """
1223
kate15f1c382016-12-15 01:12:40 -08001224 self.logger.info("Creating new instance for entry {}".format(name))
bayramov325fa1c2016-09-08 01:42:46 -07001225 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1226 format(description, start, image_id, flavor_id, net_list, cloud_config))
1227 vca = self.connect()
1228 if not vca:
1229 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1230
bayramov5761ad12016-10-04 09:00:30 +04001231 #new vm name = vmname + tenant_id + uuid
1232 new_vm_name = [name, '-', str(uuid.uuid4())]
kate15f1c382016-12-15 01:12:40 -08001233 vmname_andid = ''.join(new_vm_name)
bayramov5761ad12016-10-04 09:00:30 +04001234
bayramovef390722016-09-27 03:34:46 -07001235 # if vm already deployed we return existing uuid
bayramov5761ad12016-10-04 09:00:30 +04001236 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1237 # if vapp_uuid is not None:
1238 # return vapp_uuid
bayramov325fa1c2016-09-08 01:42:46 -07001239
bayramovef390722016-09-27 03:34:46 -07001240 # we check for presence of VDC, Catalog entry and Flavor.
1241 vdc = vca.get_vdc(self.tenant_name)
1242 if vdc is None:
bayramovfe3f3c92016-10-04 07:53:41 +04001243 raise vimconn.vimconnNotFoundException(
bayramovb6ffe792016-09-28 11:50:56 +04001244 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
bayramov325fa1c2016-09-08 01:42:46 -07001245 catalogs = vca.get_catalogs()
bayramovef390722016-09-27 03:34:46 -07001246 if catalogs is None:
bayramovfe3f3c92016-10-04 07:53:41 +04001247 raise vimconn.vimconnNotFoundException(
kate15f1c382016-12-15 01:12:40 -08001248 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
bayramovbd6160f2016-09-28 04:12:05 +04001249
kate15f1c382016-12-15 01:12:40 -08001250 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1251 if catalog_hash_name:
1252 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1253 else:
1254 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1255 "(Failed retrieve catalog information {})".format(name, image_id))
1256
1257
1258 # Set vCPU and Memory based on flavor.
1259 #
bayramovb6ffe792016-09-28 11:50:56 +04001260 vm_cpus = None
1261 vm_memory = None
bhangarea92ae392017-01-12 22:30:29 -08001262 vm_disk = None
bhangarefda5f7c2017-01-12 23:50:34 -08001263 pci_devices_info = []
bayramovb6ffe792016-09-28 11:50:56 +04001264 if flavor_id is not None:
kate15f1c382016-12-15 01:12:40 -08001265 if flavor_id not in flavorlist:
1266 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1267 "Failed retrieve flavor information "
1268 "flavor id {}".format(name, flavor_id))
bayramovb6ffe792016-09-28 11:50:56 +04001269 else:
1270 try:
kate15f1c382016-12-15 01:12:40 -08001271 flavor = flavorlist[flavor_id]
1272 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1273 vm_memory = flavor[FLAVOR_RAM_KEY]
bhangarea92ae392017-01-12 22:30:29 -08001274 vm_disk = flavor[FLAVOR_DISK_KEY]
bhangarefda5f7c2017-01-12 23:50:34 -08001275 extended = flavor.get("extended", None)
1276 if extended:
1277 numas=extended.get("numas", None)
1278 if numas:
1279 for numa in numas:
1280 for interface in numa.get("interfaces",() ):
1281 if interface["dedicated"].strip()=="yes":
1282 pci_devices_info.append(interface)
bayramovb6ffe792016-09-28 11:50:56 +04001283 except KeyError:
1284 raise vimconn.vimconnException("Corrupted flavor. {}".format(flavor_id))
bayramov325fa1c2016-09-08 01:42:46 -07001285
bayramovef390722016-09-27 03:34:46 -07001286 # image upload creates template name as catalog name space Template.
kate15f1c382016-12-15 01:12:40 -08001287 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
bayramovef390722016-09-27 03:34:46 -07001288 power_on = 'false'
1289 if start:
1290 power_on = 'true'
1291
1292 # client must provide at least one entry in net_list if not we report error
bhangare0e571a92017-01-12 04:02:23 -08001293 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1294 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1295 primary_net = None
kate15f1c382016-12-15 01:12:40 -08001296 primary_netname = None
1297 network_mode = 'bridged'
bayramovb6ffe792016-09-28 11:50:56 +04001298 if net_list is not None and len(net_list) > 0:
bhangare0e571a92017-01-12 04:02:23 -08001299 for net in net_list:
1300 if 'use' in net and net['use'] == 'mgmt':
1301 primary_net = net
bayramovb6ffe792016-09-28 11:50:56 +04001302 if primary_net is None:
bhangare0e571a92017-01-12 04:02:23 -08001303 primary_net = net_list[0]
1304
1305 try:
1306 primary_net_id = primary_net['net_id']
1307 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1308 if 'name' in network_dict:
1309 primary_netname = network_dict['name']
1310
1311 except KeyError:
1312 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1313 else:
1314 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
bayramovef390722016-09-27 03:34:46 -07001315
1316 # use: 'data', 'bridge', 'mgmt'
1317 # create vApp. Set vcpu and ram based on flavor id.
kate15f1c382016-12-15 01:12:40 -08001318 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
bayramovef390722016-09-27 03:34:46 -07001319 self.get_catalogbyid(image_id, catalogs),
bhangare0e571a92017-01-12 04:02:23 -08001320 network_name=None, # None while creating vapp
kate15f1c382016-12-15 01:12:40 -08001321 network_mode=network_mode,
1322 vm_name=vmname_andid,
bayramovfe3f3c92016-10-04 07:53:41 +04001323 vm_cpus=vm_cpus, # can be None if flavor is None
1324 vm_memory=vm_memory) # can be None if flavor is None
bayramovef390722016-09-27 03:34:46 -07001325
1326 if vapptask is None or vapptask is False:
kate15f1c382016-12-15 01:12:40 -08001327 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid))
bayramovef390722016-09-27 03:34:46 -07001328 if type(vapptask) is VappTask:
1329 vca.block_until_completed(vapptask)
1330
1331 # we should have now vapp in undeployed state.
kate15f1c382016-12-15 01:12:40 -08001332 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
bhangarea92ae392017-01-12 22:30:29 -08001333 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
bayramovef390722016-09-27 03:34:46 -07001334 if vapp is None:
bayramovbd6160f2016-09-28 04:12:05 +04001335 raise vimconn.vimconnUnexpectedResponse(
bhangarea92ae392017-01-12 22:30:29 -08001336 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1337 vmname_andid))
1338
bhangarefda5f7c2017-01-12 23:50:34 -08001339 #Add PCI passthrough configrations
1340 PCI_devices_status = False
1341 vm_obj = None
1342 si = None
1343 if len(pci_devices_info) > 0:
1344 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1345 vmname_andid ))
1346 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1347 pci_devices_info,
1348 vmname_andid)
1349 if PCI_devices_status:
1350 self.logger.info("Added PCI devives {} to VM {}".format(
1351 pci_devices_info,
1352 vmname_andid)
1353 )
1354 else:
1355 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1356 pci_devices_info,
1357 vmname_andid)
1358 )
bhangarea92ae392017-01-12 22:30:29 -08001359 # add vm disk
1360 if vm_disk:
1361 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1362 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1363 if result :
1364 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
bayramovef390722016-09-27 03:34:46 -07001365
bhangare0e571a92017-01-12 04:02:23 -08001366 # add NICs & connect to networks in netlist
bayramovef390722016-09-27 03:34:46 -07001367 try:
kate15f1c382016-12-15 01:12:40 -08001368 self.logger.info("Request to connect VM to a network: {}".format(net_list))
bayramovef390722016-09-27 03:34:46 -07001369 nicIndex = 0
bhangare0e571a92017-01-12 04:02:23 -08001370 primary_nic_index = 0
bayramovef390722016-09-27 03:34:46 -07001371 for net in net_list:
1372 # openmano uses network id in UUID format.
1373 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
kate15f1c382016-12-15 01:12:40 -08001374 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1375 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1376
1377 if 'net_id' not in net:
1378 continue
1379
bayramovef390722016-09-27 03:34:46 -07001380 interface_net_id = net['net_id']
kate15f1c382016-12-15 01:12:40 -08001381 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
bayramovef390722016-09-27 03:34:46 -07001382 interface_network_mode = net['use']
1383
bhangare0e571a92017-01-12 04:02:23 -08001384 if interface_network_mode == 'mgmt':
1385 primary_nic_index = nicIndex
1386
kate15f1c382016-12-15 01:12:40 -08001387 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1388 - DHCP (The IP address is obtained from a DHCP service.)
1389 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1390 - NONE (No IP addressing mode specified.)"""
1391
1392 if primary_netname is not None:
bayramovef390722016-09-27 03:34:46 -07001393 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1394 if len(nets) == 1:
bhangare0e571a92017-01-12 04:02:23 -08001395 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
bayramovef390722016-09-27 03:34:46 -07001396 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1397 if type(task) is GenericTask:
1398 vca.block_until_completed(task)
bhangare0e571a92017-01-12 04:02:23 -08001399 # connect network to VM - with all DHCP by default
1400 self.logger.info("new_vminstance(): Connecting VM to a network {}".format(nets[0].name))
kate15f1c382016-12-15 01:12:40 -08001401 task = vapp.connect_vms(nets[0].name,
1402 connection_index=nicIndex,
bhangare0e571a92017-01-12 04:02:23 -08001403 connections_primary_index=primary_nic_index,
bayramovef390722016-09-27 03:34:46 -07001404 ip_allocation_mode='DHCP')
1405 if type(task) is GenericTask:
1406 vca.block_until_completed(task)
bhangare0e571a92017-01-12 04:02:23 -08001407 nicIndex += 1
bayramovef390722016-09-27 03:34:46 -07001408 except KeyError:
bayramovef390722016-09-27 03:34:46 -07001409 # it might be a case if specific mandatory entry in dict is empty
1410 self.logger.debug("Key error {}".format(KeyError.message))
bayramovbd6160f2016-09-28 04:12:05 +04001411 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
bayramovef390722016-09-27 03:34:46 -07001412
1413 # deploy and power on vm
bhangarefda5f7c2017-01-12 23:50:34 -08001414 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1415 deploytask = vapp.deploy(powerOn=False)
1416 if type(deploytask) is GenericTask:
bayramovef390722016-09-27 03:34:46 -07001417 vca.block_until_completed(deploytask)
1418
bhangarefda5f7c2017-01-12 23:50:34 -08001419 # If VM has PCI devices reserve memory for VM
1420 if PCI_devices_status and vm_obj and vcenter_conect:
1421 memReserve = vm_obj.config.hardware.memoryMB
1422 spec = vim.vm.ConfigSpec()
1423 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1424 task = vm_obj.ReconfigVM_Task(spec=spec)
1425 if task:
1426 result = self.wait_for_vcenter_task(task, vcenter_conect)
1427 self.logger.info("Reserved memmoery {} MB for "\
1428 "VM VM status: {}".format(str(memReserve),result))
1429 else:
1430 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1431 str(memReserve),str(vm_obj)))
1432
1433 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1434 poweron_task = vapp.poweron()
1435 if type(poweron_task) is GenericTask:
1436 vca.block_until_completed(poweron_task)
1437
bayramovef390722016-09-27 03:34:46 -07001438 # check if vApp deployed and if that the case return vApp UUID otherwise -1
kate13ab2c42016-12-23 01:34:24 -08001439 wait_time = 0
1440 vapp_uuid = None
1441 while wait_time <= MAX_WAIT_TIME:
1442 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1443 if vapp and vapp.me.deployed:
1444 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1445 break
1446 else:
1447 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1448 time.sleep(INTERVAL_TIME)
1449
1450 wait_time +=INTERVAL_TIME
1451
bayramovef390722016-09-27 03:34:46 -07001452 if vapp_uuid is not None:
1453 return vapp_uuid
bayramovbd6160f2016-09-28 04:12:05 +04001454 else:
1455 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
bayramov325fa1c2016-09-08 01:42:46 -07001456
bayramovef390722016-09-27 03:34:46 -07001457 ##
1458 ##
1459 ## based on current discussion
1460 ##
1461 ##
1462 ## server:
1463 # created: '2016-09-08T11:51:58'
1464 # description: simple-instance.linux1.1
1465 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1466 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1467 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1468 # status: ACTIVE
1469 # error_msg:
1470 # interfaces: …
1471 #
bayramovfe3f3c92016-10-04 07:53:41 +04001472 def get_vminstance(self, vim_vm_uuid=None):
bayramov5761ad12016-10-04 09:00:30 +04001473 """Returns the VM instance information from VIM"""
bayramov325fa1c2016-09-08 01:42:46 -07001474
bayramovef390722016-09-27 03:34:46 -07001475 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
bayramov325fa1c2016-09-08 01:42:46 -07001476 vca = self.connect()
1477 if not vca:
1478 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1479
bayramovef390722016-09-27 03:34:46 -07001480 vdc = vca.get_vdc(self.tenant_name)
1481 if vdc is None:
bayramovfe3f3c92016-10-04 07:53:41 +04001482 raise vimconn.vimconnConnectionException(
1483 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
bayramov325fa1c2016-09-08 01:42:46 -07001484
bayramovfe3f3c92016-10-04 07:53:41 +04001485 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1486 if not vm_info_dict:
bayramovef390722016-09-27 03:34:46 -07001487 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
bayramovfe3f3c92016-10-04 07:53:41 +04001488 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
bayramov325fa1c2016-09-08 01:42:46 -07001489
bayramovfe3f3c92016-10-04 07:53:41 +04001490 status_key = vm_info_dict['status']
1491 error = ''
bayramovef390722016-09-27 03:34:46 -07001492 try:
bayramovfe3f3c92016-10-04 07:53:41 +04001493 vm_dict = {'created': vm_info_dict['created'],
1494 'description': vm_info_dict['name'],
1495 'status': vcdStatusCode2manoFormat[int(status_key)],
1496 'hostId': vm_info_dict['vmuuid'],
1497 'error_msg': error,
1498 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
bayramovef390722016-09-27 03:34:46 -07001499
bayramov5761ad12016-10-04 09:00:30 +04001500 if 'interfaces' in vm_info_dict:
bayramovfe3f3c92016-10-04 07:53:41 +04001501 vm_dict['interfaces'] = vm_info_dict['interfaces']
1502 else:
1503 vm_dict['interfaces'] = []
1504 except KeyError:
1505 vm_dict = {'created': '',
1506 'description': '',
1507 'status': vcdStatusCode2manoFormat[int(-1)],
1508 'hostId': vm_info_dict['vmuuid'],
1509 'error_msg': "Inconsistency state",
1510 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
bayramovef390722016-09-27 03:34:46 -07001511
1512 return vm_dict
1513
1514 def delete_vminstance(self, vm__vim_uuid):
1515 """Method poweroff and remove VM instance from vcloud director network.
1516
1517 Args:
1518 vm__vim_uuid: VM UUID
1519
1520 Returns:
1521 Returns the instance identifier
1522 """
1523
1524 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
bayramov325fa1c2016-09-08 01:42:46 -07001525 vca = self.connect()
1526 if not vca:
1527 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1528
bayramovef390722016-09-27 03:34:46 -07001529 vdc = vca.get_vdc(self.tenant_name)
1530 if vdc is None:
1531 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1532 self.tenant_name))
bayramovbd6160f2016-09-28 04:12:05 +04001533 raise vimconn.vimconnException(
1534 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
bayramov325fa1c2016-09-08 01:42:46 -07001535
bayramovef390722016-09-27 03:34:46 -07001536 try:
1537 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1538 if vapp_name is None:
1539 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1540 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1541 else:
1542 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
bayramov325fa1c2016-09-08 01:42:46 -07001543
bayramovef390722016-09-27 03:34:46 -07001544 # Delete vApp and wait for status change if task executed and vApp is None.
bayramovef390722016-09-27 03:34:46 -07001545 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
bayramov325fa1c2016-09-08 01:42:46 -07001546
kate13ab2c42016-12-23 01:34:24 -08001547 if vapp:
1548 if vapp.me.deployed:
1549 self.logger.info("Powering off vApp {}".format(vapp_name))
1550 #Power off vApp
1551 powered_off = False
1552 wait_time = 0
1553 while wait_time <= MAX_WAIT_TIME:
1554 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1555 if not vapp:
1556 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1557 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
bayramovef390722016-09-27 03:34:46 -07001558
kate13ab2c42016-12-23 01:34:24 -08001559 power_off_task = vapp.poweroff()
1560 if type(power_off_task) is GenericTask:
1561 result = vca.block_until_completed(power_off_task)
1562 if result:
1563 powered_off = True
1564 break
1565 else:
1566 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1567 time.sleep(INTERVAL_TIME)
1568
1569 wait_time +=INTERVAL_TIME
1570 if not powered_off:
1571 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1572 else:
1573 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1574
1575 #Undeploy vApp
1576 self.logger.info("Undeploy vApp {}".format(vapp_name))
1577 wait_time = 0
1578 undeployed = False
1579 while wait_time <= MAX_WAIT_TIME:
1580 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1581 if not vapp:
1582 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1583 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1584 undeploy_task = vapp.undeploy(action='powerOff')
1585
1586 if type(undeploy_task) is GenericTask:
1587 result = vca.block_until_completed(undeploy_task)
1588 if result:
1589 undeployed = True
1590 break
1591 else:
1592 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1593 time.sleep(INTERVAL_TIME)
1594
1595 wait_time +=INTERVAL_TIME
1596
1597 if not undeployed:
1598 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1599
1600 # delete vapp
1601 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1602 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1603
1604 if vapp is not None:
1605 wait_time = 0
1606 result = False
1607
1608 while wait_time <= MAX_WAIT_TIME:
1609 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1610 if not vapp:
1611 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1612 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1613
1614 delete_task = vapp.delete()
1615
1616 if type(delete_task) is GenericTask:
1617 vca.block_until_completed(delete_task)
1618 result = vca.block_until_completed(delete_task)
1619 if result:
1620 break
1621 else:
1622 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1623 time.sleep(INTERVAL_TIME)
1624
1625 wait_time +=INTERVAL_TIME
1626
1627 if not result:
bayramovbd6160f2016-09-28 04:12:05 +04001628 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
bayramovef390722016-09-27 03:34:46 -07001629
bayramovef390722016-09-27 03:34:46 -07001630 except:
1631 self.logger.debug(traceback.format_exc())
bayramovbd6160f2016-09-28 04:12:05 +04001632 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
bayramovef390722016-09-27 03:34:46 -07001633
bayramovbd6160f2016-09-28 04:12:05 +04001634 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
kate13ab2c42016-12-23 01:34:24 -08001635 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
bayramovbd6160f2016-09-28 04:12:05 +04001636 return vm__vim_uuid
1637 else:
1638 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
bayramov325fa1c2016-09-08 01:42:46 -07001639
1640 def refresh_vms_status(self, vm_list):
bayramovef390722016-09-27 03:34:46 -07001641 """Get the status of the virtual machines and their interfaces/ports
bayramov325fa1c2016-09-08 01:42:46 -07001642 Params: the list of VM identifiers
1643 Returns a dictionary with:
1644 vm_id: #VIM id of this Virtual Machine
1645 status: #Mandatory. Text with one of:
1646 # DELETED (not found at vim)
bayramovef390722016-09-27 03:34:46 -07001647 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
bayramov325fa1c2016-09-08 01:42:46 -07001648 # OTHER (Vim reported other status not understood)
1649 # ERROR (VIM indicates an ERROR status)
bayramovef390722016-09-27 03:34:46 -07001650 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
bayramov325fa1c2016-09-08 01:42:46 -07001651 # CREATING (on building process), ERROR
1652 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1653 #
bayramovef390722016-09-27 03:34:46 -07001654 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
bayramov325fa1c2016-09-08 01:42:46 -07001655 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1656 interfaces:
1657 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1658 mac_address: #Text format XX:XX:XX:XX:XX:XX
1659 vim_net_id: #network id where this interface is connected
1660 vim_interface_id: #interface/port VIM id
1661 ip_address: #null, or text with IPv4, IPv6 address
bayramovef390722016-09-27 03:34:46 -07001662 """
bayramov325fa1c2016-09-08 01:42:46 -07001663
bayramovef390722016-09-27 03:34:46 -07001664 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
bhangare985a1fd2017-01-31 01:53:21 -08001665
1666 mac_ip_addr={}
1667 rheaders = {'Content-Type': 'application/xml'}
1668
1669 try:
1670 resp = requests.get(self.nsx_manager + '/api/4.0/edges/edge-2/dhcp/leaseInfo',
1671 auth = (self.nsx_user, self.nsx_password),
1672 verify = False, headers = rheaders)
1673
1674 if resp.status_code == requests.codes.ok:
1675 dhcp_leases = XmlElementTree.fromstring(resp.text)
1676 for child in dhcp_leases:
1677 if child.tag == 'dhcpLeaseInfo':
1678 dhcpLeaseInfo = child
1679 for leaseInfo in dhcpLeaseInfo:
1680 for elem in leaseInfo:
1681 if (elem.tag)=='macAddress':
1682 mac_addr = elem.text
1683 if (elem.tag)=='ipAddress':
1684 ip_addr = elem.text
1685 if (mac_addr) is not None:
1686 mac_ip_addr[mac_addr]= ip_addr
1687 self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr))
1688 else:
1689 self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content))
1690 except KeyError:
1691 self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message))
1692 self.logger.debug(traceback.format_exc())
1693
bayramovef390722016-09-27 03:34:46 -07001694 vca = self.connect()
1695 if not vca:
1696 raise vimconn.vimconnConnectionException("self.connect() is failed.")
bayramov325fa1c2016-09-08 01:42:46 -07001697
bayramovef390722016-09-27 03:34:46 -07001698 vdc = vca.get_vdc(self.tenant_name)
1699 if vdc is None:
bayramovbd6160f2016-09-28 04:12:05 +04001700 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
bayramovef390722016-09-27 03:34:46 -07001701
1702 vms_dict = {}
1703 for vmuuid in vm_list:
1704 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1705 if vmname is not None:
1706
1707 the_vapp = vca.get_vapp(vdc, vmname)
1708 vm_info = the_vapp.get_vms_details()
1709 vm_status = vm_info[0]['status']
bhangarefda5f7c2017-01-12 23:50:34 -08001710 vm_pci_details = self.get_vm_pci_details(vmuuid)
1711 vm_info[0].update(vm_pci_details)
bayramovef390722016-09-27 03:34:46 -07001712
bayramov5761ad12016-10-04 09:00:30 +04001713 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1714 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
bhangarefda5f7c2017-01-12 23:50:34 -08001715 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
bayramovef390722016-09-27 03:34:46 -07001716
1717 # get networks
1718 try:
1719 vm_app_networks = the_vapp.get_vms_network_info()
1720 for vapp_network in vm_app_networks:
1721 for vm_network in vapp_network:
1722 if vm_network['name'] == vmname:
bhangare985a1fd2017-01-31 01:53:21 -08001723 #Assign IP Address based on MAC Address in NSX DHCP lease info
1724 for mac_adres,ip_adres in mac_ip_addr.iteritems():
1725 if mac_adres == vm_network['mac']:
1726 vm_network['ip']=ip_adres
bayramov5761ad12016-10-04 09:00:30 +04001727 interface = {"mac_address": vm_network['mac'],
bhangare2c855072016-12-27 01:41:28 -08001728 "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
1729 "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
bayramov5761ad12016-10-04 09:00:30 +04001730 'ip_address': vm_network['ip']}
bayramovef390722016-09-27 03:34:46 -07001731 # interface['vim_info'] = yaml.safe_dump(vm_network)
bayramovef390722016-09-27 03:34:46 -07001732 vm_dict["interfaces"].append(interface)
1733 # add a vm to vm dict
1734 vms_dict.setdefault(vmuuid, vm_dict)
1735 except KeyError:
1736 self.logger.debug("Error in respond {}".format(KeyError.message))
1737 self.logger.debug(traceback.format_exc())
1738
1739 return vms_dict
1740
1741 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1742 """Send and action over a VM instance from VIM
1743 Returns the vm_id if the action was successfully sent to the VIM"""
1744
1745 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1746 if vm__vim_uuid is None or action_dict is None:
bayramovbd6160f2016-09-28 04:12:05 +04001747 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
bayramovef390722016-09-27 03:34:46 -07001748
1749 vca = self.connect()
1750 if not vca:
1751 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1752
1753 vdc = vca.get_vdc(self.tenant_name)
1754 if vdc is None:
1755 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1756
1757 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1758 if vapp_name is None:
1759 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
bayramovbd6160f2016-09-28 04:12:05 +04001760 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
bayramovef390722016-09-27 03:34:46 -07001761 else:
1762 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1763
1764 try:
1765 the_vapp = vca.get_vapp(vdc, vapp_name)
1766 # TODO fix all status
1767 if "start" in action_dict:
bhangare985a1fd2017-01-31 01:53:21 -08001768 vm_info = the_vapp.get_vms_details()
1769 vm_status = vm_info[0]['status']
1770 self.logger.info("Power on vApp: vm_status:{} {}".format(type(vm_status),vm_status))
1771 if vm_status == "Suspended" or vm_status == "Powered off":
1772 power_on_task = the_vapp.poweron()
1773 if power_on_task is not None and type(power_on_task) is GenericTask:
1774 result = vca.block_until_completed(power_on_task)
1775 if result:
1776 self.logger.info("action_vminstance: Powered on vApp: {}".format(vapp_name))
1777 else:
1778 self.logger.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name))
1779 else:
1780 self.logger.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name))
1781 elif "rebuild" in action_dict:
1782 self.logger.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name))
1783 power_on_task = the_vapp.deploy(powerOn=True)
1784 if type(power_on_task) is GenericTask:
1785 result = vca.block_until_completed(power_on_task)
1786 if result:
1787 self.logger.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name))
1788 else:
1789 self.logger.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name))
bayramovef390722016-09-27 03:34:46 -07001790 else:
bhangare985a1fd2017-01-31 01:53:21 -08001791 self.logger.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name))
bayramovef390722016-09-27 03:34:46 -07001792 elif "pause" in action_dict:
1793 pass
bayramovb6ffe792016-09-28 11:50:56 +04001794 ## server.pause()
bayramovef390722016-09-27 03:34:46 -07001795 elif "resume" in action_dict:
1796 pass
bayramovb6ffe792016-09-28 11:50:56 +04001797 ## server.resume()
bayramovef390722016-09-27 03:34:46 -07001798 elif "shutoff" in action_dict or "shutdown" in action_dict:
bhangare985a1fd2017-01-31 01:53:21 -08001799 power_off_task = the_vapp.undeploy(action='powerOff')
1800 if type(power_off_task) is GenericTask:
1801 result = vca.block_until_completed(power_off_task)
1802 if result:
1803 self.logger.info("action_vminstance: Powered off vApp: {}".format(vapp_name))
1804 else:
1805 self.logger.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name))
1806 else:
1807 self.logger.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name))
bayramovef390722016-09-27 03:34:46 -07001808 elif "forceOff" in action_dict:
1809 the_vapp.reset()
1810 elif "terminate" in action_dict:
1811 the_vapp.delete()
1812 # elif "createImage" in action_dict:
1813 # server.create_image()
1814 else:
1815 pass
1816 except:
1817 pass
1818
1819 def get_vminstance_console(self, vm_id, console_type="vnc"):
1820 """
bayramov325fa1c2016-09-08 01:42:46 -07001821 Get a console for the virtual machine
1822 Params:
1823 vm_id: uuid of the VM
1824 console_type, can be:
bayramovef390722016-09-27 03:34:46 -07001825 "novnc" (by default), "xvpvnc" for VNC types,
bayramov325fa1c2016-09-08 01:42:46 -07001826 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1827 Returns dict with the console parameters:
1828 protocol: ssh, ftp, http, https, ...
bayramovef390722016-09-27 03:34:46 -07001829 server: usually ip address
1830 port: the http, ssh, ... port
1831 suffix: extra text, e.g. the http path and query string
1832 """
bayramovbd6160f2016-09-28 04:12:05 +04001833 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001834
bayramovef390722016-09-27 03:34:46 -07001835 # NOT USED METHODS in current version
bayramov325fa1c2016-09-08 01:42:46 -07001836
1837 def host_vim2gui(self, host, server_dict):
bayramov5761ad12016-10-04 09:00:30 +04001838 """Transform host dictionary from VIM format to GUI format,
bayramov325fa1c2016-09-08 01:42:46 -07001839 and append to the server_dict
bayramov5761ad12016-10-04 09:00:30 +04001840 """
bayramovbd6160f2016-09-28 04:12:05 +04001841 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001842
1843 def get_hosts_info(self):
bayramov5761ad12016-10-04 09:00:30 +04001844 """Get the information of deployed hosts
1845 Returns the hosts content"""
bayramovbd6160f2016-09-28 04:12:05 +04001846 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001847
1848 def get_hosts(self, vim_tenant):
bayramov5761ad12016-10-04 09:00:30 +04001849 """Get the hosts and deployed instances
1850 Returns the hosts content"""
bayramovbd6160f2016-09-28 04:12:05 +04001851 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001852
1853 def get_processor_rankings(self):
bayramov5761ad12016-10-04 09:00:30 +04001854 """Get the processor rankings in the VIM database"""
bayramovbd6160f2016-09-28 04:12:05 +04001855 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001856
1857 def new_host(self, host_data):
bayramov5761ad12016-10-04 09:00:30 +04001858 """Adds a new host to VIM"""
bayramov325fa1c2016-09-08 01:42:46 -07001859 '''Returns status code of the VIM response'''
bayramovbd6160f2016-09-28 04:12:05 +04001860 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001861
1862 def new_external_port(self, port_data):
bayramov5761ad12016-10-04 09:00:30 +04001863 """Adds a external port to VIM"""
bayramov325fa1c2016-09-08 01:42:46 -07001864 '''Returns the port identifier'''
bayramovbd6160f2016-09-28 04:12:05 +04001865 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001866
bayramovef390722016-09-27 03:34:46 -07001867 def new_external_network(self, net_name, net_type):
bayramov5761ad12016-10-04 09:00:30 +04001868 """Adds a external network to VIM (shared)"""
bayramov325fa1c2016-09-08 01:42:46 -07001869 '''Returns the network identifier'''
bayramovbd6160f2016-09-28 04:12:05 +04001870 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001871
1872 def connect_port_network(self, port_id, network_id, admin=False):
bayramov5761ad12016-10-04 09:00:30 +04001873 """Connects a external port to a network"""
bayramov325fa1c2016-09-08 01:42:46 -07001874 '''Returns status code of the VIM response'''
bayramovbd6160f2016-09-28 04:12:05 +04001875 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001876
1877 def new_vminstancefromJSON(self, vm_data):
bayramov5761ad12016-10-04 09:00:30 +04001878 """Adds a VM instance to VIM"""
bayramov325fa1c2016-09-08 01:42:46 -07001879 '''Returns the instance identifier'''
bayramovbd6160f2016-09-28 04:12:05 +04001880 raise vimconn.vimconnNotImplemented("Should have implemented this")
bayramov325fa1c2016-09-08 01:42:46 -07001881
kate15f1c382016-12-15 01:12:40 -08001882 def get_network_name_by_id(self, network_uuid=None):
bayramovef390722016-09-27 03:34:46 -07001883 """Method gets vcloud director network named based on supplied uuid.
1884
1885 Args:
kate15f1c382016-12-15 01:12:40 -08001886 network_uuid: network_id
bayramovef390722016-09-27 03:34:46 -07001887
1888 Returns:
1889 The return network name.
1890 """
1891
1892 vca = self.connect()
1893 if not vca:
kate15f1c382016-12-15 01:12:40 -08001894 raise vimconn.vimconnConnectionException("self.connect() is failed.")
bayramovef390722016-09-27 03:34:46 -07001895
kate15f1c382016-12-15 01:12:40 -08001896 if not network_uuid:
bayramovef390722016-09-27 03:34:46 -07001897 return None
1898
1899 try:
kate15f1c382016-12-15 01:12:40 -08001900 org_dict = self.get_org(self.org_uuid)
1901 if 'networks' in org_dict:
1902 org_network_dict = org_dict['networks']
1903 for net_uuid in org_network_dict:
1904 if net_uuid == network_uuid:
1905 return org_network_dict[net_uuid]
bayramovef390722016-09-27 03:34:46 -07001906 except:
1907 self.logger.debug("Exception in get_network_name_by_id")
1908 self.logger.debug(traceback.format_exc())
1909
1910 return None
1911
bhangare2c855072016-12-27 01:41:28 -08001912 def get_network_id_by_name(self, network_name=None):
1913 """Method gets vcloud director network uuid based on supplied name.
1914
1915 Args:
1916 network_name: network_name
1917 Returns:
1918 The return network uuid.
1919 network_uuid: network_id
1920 """
1921
1922 vca = self.connect()
1923 if not vca:
1924 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1925
1926 if not network_name:
1927 self.logger.debug("get_network_id_by_name() : Network name is empty")
1928 return None
1929
1930 try:
1931 org_dict = self.get_org(self.org_uuid)
1932 if org_dict and 'networks' in org_dict:
1933 org_network_dict = org_dict['networks']
1934 for net_uuid,net_name in org_network_dict.iteritems():
1935 if net_name == network_name:
1936 return net_uuid
1937
1938 except KeyError as exp:
1939 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
1940
1941 return None
1942
bayramovef390722016-09-27 03:34:46 -07001943 def list_org_action(self):
1944 """
1945 Method leverages vCloud director and query for available organization for particular user
1946
1947 Args:
1948 vca - is active VCA connection.
1949 vdc_name - is a vdc name that will be used to query vms action
1950
1951 Returns:
1952 The return XML respond
1953 """
1954
1955 vca = self.connect()
1956 if not vca:
1957 raise vimconn.vimconnConnectionException("self.connect() is failed")
1958
1959 url_list = [vca.host, '/api/org']
1960 vm_list_rest_call = ''.join(url_list)
1961
1962 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1963 response = Http.get(url=vm_list_rest_call,
1964 headers=vca.vcloud_session.get_vcloud_headers(),
1965 verify=vca.verify,
1966 logger=vca.logger)
1967 if response.status_code == requests.codes.ok:
1968 return response.content
1969
1970 return None
1971
1972 def get_org_action(self, org_uuid=None):
1973 """
1974 Method leverages vCloud director and retrieve available object fdr organization.
1975
1976 Args:
1977 vca - is active VCA connection.
1978 vdc_name - is a vdc name that will be used to query vms action
1979
1980 Returns:
1981 The return XML respond
1982 """
1983
1984 vca = self.connect()
1985 if not vca:
1986 raise vimconn.vimconnConnectionException("self.connect() is failed")
1987
1988 if org_uuid is None:
1989 return None
1990
1991 url_list = [vca.host, '/api/org/', org_uuid]
1992 vm_list_rest_call = ''.join(url_list)
1993
1994 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1995 response = Http.get(url=vm_list_rest_call,
1996 headers=vca.vcloud_session.get_vcloud_headers(),
1997 verify=vca.verify,
1998 logger=vca.logger)
1999 if response.status_code == requests.codes.ok:
2000 return response.content
2001
2002 return None
2003
2004 def get_org(self, org_uuid=None):
2005 """
2006 Method retrieves available organization in vCloud Director
2007
2008 Args:
bayramovb6ffe792016-09-28 11:50:56 +04002009 org_uuid - is a organization uuid.
bayramovef390722016-09-27 03:34:46 -07002010
2011 Returns:
bayramovb6ffe792016-09-28 11:50:56 +04002012 The return dictionary with following key
2013 "network" - for network list under the org
2014 "catalogs" - for network list under the org
2015 "vdcs" - for vdc list under org
bayramovef390722016-09-27 03:34:46 -07002016 """
2017
2018 org_dict = {}
bayramovef390722016-09-27 03:34:46 -07002019 vca = self.connect()
2020 if not vca:
2021 raise vimconn.vimconnConnectionException("self.connect() is failed")
2022
2023 if org_uuid is None:
2024 return org_dict
2025
2026 content = self.get_org_action(org_uuid=org_uuid)
2027 try:
2028 vdc_list = {}
2029 network_list = {}
2030 catalog_list = {}
2031 vm_list_xmlroot = XmlElementTree.fromstring(content)
2032 for child in vm_list_xmlroot:
2033 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2034 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2035 org_dict['vdcs'] = vdc_list
2036 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2037 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2038 org_dict['networks'] = network_list
2039 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2040 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2041 org_dict['catalogs'] = catalog_list
2042 except:
2043 pass
2044
2045 return org_dict
2046
2047 def get_org_list(self):
2048 """
2049 Method retrieves available organization in vCloud Director
2050
2051 Args:
2052 vca - is active VCA connection.
2053
2054 Returns:
2055 The return dictionary and key for each entry VDC UUID
2056 """
2057
2058 org_dict = {}
2059 vca = self.connect()
2060 if not vca:
2061 raise vimconn.vimconnConnectionException("self.connect() is failed")
2062
2063 content = self.list_org_action()
2064 try:
2065 vm_list_xmlroot = XmlElementTree.fromstring(content)
2066 for vm_xml in vm_list_xmlroot:
2067 if vm_xml.tag.split("}")[1] == 'Org':
2068 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2069 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2070 except:
2071 pass
2072
2073 return org_dict
2074
2075 def vms_view_action(self, vdc_name=None):
2076 """ Method leverages vCloud director vms query call
2077
2078 Args:
2079 vca - is active VCA connection.
2080 vdc_name - is a vdc name that will be used to query vms action
2081
2082 Returns:
2083 The return XML respond
2084 """
2085 vca = self.connect()
2086 if vdc_name is None:
2087 return None
2088
2089 url_list = [vca.host, '/api/vms/query']
2090 vm_list_rest_call = ''.join(url_list)
2091
2092 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2093 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
2094 vca.vcloud_session.organization.Link)
2095 if len(refs) == 1:
2096 response = Http.get(url=vm_list_rest_call,
2097 headers=vca.vcloud_session.get_vcloud_headers(),
2098 verify=vca.verify,
2099 logger=vca.logger)
2100 if response.status_code == requests.codes.ok:
2101 return response.content
2102
2103 return None
2104
2105 def get_vapp_list(self, vdc_name=None):
2106 """
2107 Method retrieves vApp list deployed vCloud director and returns a dictionary
2108 contains a list of all vapp deployed for queried VDC.
2109 The key for a dictionary is vApp UUID
2110
2111
2112 Args:
2113 vca - is active VCA connection.
2114 vdc_name - is a vdc name that will be used to query vms action
2115
2116 Returns:
2117 The return dictionary and key for each entry vapp UUID
2118 """
2119
2120 vapp_dict = {}
2121 if vdc_name is None:
2122 return vapp_dict
2123
2124 content = self.vms_view_action(vdc_name=vdc_name)
2125 try:
2126 vm_list_xmlroot = XmlElementTree.fromstring(content)
2127 for vm_xml in vm_list_xmlroot:
2128 if vm_xml.tag.split("}")[1] == 'VMRecord':
2129 if vm_xml.attrib['isVAppTemplate'] == 'true':
2130 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2131 if 'vappTemplate-' in rawuuid[0]:
2132 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2133 # vm and use raw UUID as key
2134 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2135 except:
2136 pass
2137
2138 return vapp_dict
2139
2140 def get_vm_list(self, vdc_name=None):
2141 """
2142 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2143 contains a list of all VM's deployed for queried VDC.
2144 The key for a dictionary is VM UUID
2145
2146
2147 Args:
2148 vca - is active VCA connection.
2149 vdc_name - is a vdc name that will be used to query vms action
2150
2151 Returns:
2152 The return dictionary and key for each entry vapp UUID
2153 """
2154 vm_dict = {}
2155
2156 if vdc_name is None:
2157 return vm_dict
2158
2159 content = self.vms_view_action(vdc_name=vdc_name)
2160 try:
2161 vm_list_xmlroot = XmlElementTree.fromstring(content)
2162 for vm_xml in vm_list_xmlroot:
2163 if vm_xml.tag.split("}")[1] == 'VMRecord':
2164 if vm_xml.attrib['isVAppTemplate'] == 'false':
2165 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2166 if 'vm-' in rawuuid[0]:
2167 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2168 # vm and use raw UUID as key
2169 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2170 except:
2171 pass
2172
2173 return vm_dict
2174
2175 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2176 """
bayramovb6ffe792016-09-28 11:50:56 +04002177 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
bayramovef390722016-09-27 03:34:46 -07002178 contains a list of all VM's deployed for queried VDC.
2179 The key for a dictionary is VM UUID
2180
2181
2182 Args:
2183 vca - is active VCA connection.
2184 vdc_name - is a vdc name that will be used to query vms action
2185
2186 Returns:
2187 The return dictionary and key for each entry vapp UUID
2188 """
2189 vm_dict = {}
2190 vca = self.connect()
2191 if not vca:
2192 raise vimconn.vimconnConnectionException("self.connect() is failed")
2193
2194 if vdc_name is None:
2195 return vm_dict
2196
2197 content = self.vms_view_action(vdc_name=vdc_name)
2198 try:
2199 vm_list_xmlroot = XmlElementTree.fromstring(content)
2200 for vm_xml in vm_list_xmlroot:
bayramovb6ffe792016-09-28 11:50:56 +04002201 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2202 # lookup done by UUID
bayramovef390722016-09-27 03:34:46 -07002203 if isuuid:
bayramovef390722016-09-27 03:34:46 -07002204 if vapp_name in vm_xml.attrib['container']:
2205 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2206 if 'vm-' in rawuuid[0]:
bayramovef390722016-09-27 03:34:46 -07002207 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
bayramovb6ffe792016-09-28 11:50:56 +04002208 break
2209 # lookup done by Name
2210 else:
2211 if vapp_name in vm_xml.attrib['name']:
2212 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2213 if 'vm-' in rawuuid[0]:
2214 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2215 break
bayramovef390722016-09-27 03:34:46 -07002216 except:
2217 pass
2218
2219 return vm_dict
2220
2221 def get_network_action(self, network_uuid=None):
2222 """
2223 Method leverages vCloud director and query network based on network uuid
2224
2225 Args:
2226 vca - is active VCA connection.
2227 network_uuid - is a network uuid
2228
2229 Returns:
2230 The return XML respond
2231 """
2232
2233 vca = self.connect()
2234 if not vca:
2235 raise vimconn.vimconnConnectionException("self.connect() is failed")
2236
2237 if network_uuid is None:
2238 return None
2239
2240 url_list = [vca.host, '/api/network/', network_uuid]
2241 vm_list_rest_call = ''.join(url_list)
2242
2243 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2244 response = Http.get(url=vm_list_rest_call,
2245 headers=vca.vcloud_session.get_vcloud_headers(),
2246 verify=vca.verify,
2247 logger=vca.logger)
2248 if response.status_code == requests.codes.ok:
2249 return response.content
2250
2251 return None
2252
2253 def get_vcd_network(self, network_uuid=None):
2254 """
2255 Method retrieves available network from vCloud Director
2256
2257 Args:
2258 network_uuid - is VCD network UUID
2259
2260 Each element serialized as key : value pair
2261
2262 Following keys available for access. network_configuration['Gateway'}
2263 <Configuration>
2264 <IpScopes>
2265 <IpScope>
2266 <IsInherited>true</IsInherited>
2267 <Gateway>172.16.252.100</Gateway>
2268 <Netmask>255.255.255.0</Netmask>
2269 <Dns1>172.16.254.201</Dns1>
2270 <Dns2>172.16.254.202</Dns2>
2271 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2272 <IsEnabled>true</IsEnabled>
2273 <IpRanges>
2274 <IpRange>
2275 <StartAddress>172.16.252.1</StartAddress>
2276 <EndAddress>172.16.252.99</EndAddress>
2277 </IpRange>
2278 </IpRanges>
2279 </IpScope>
2280 </IpScopes>
2281 <FenceMode>bridged</FenceMode>
2282
2283 Returns:
2284 The return dictionary and key for each entry vapp UUID
2285 """
2286
2287 network_configuration = {}
2288 if network_uuid is None:
2289 return network_uuid
2290
2291 content = self.get_network_action(network_uuid=network_uuid)
2292 try:
2293 vm_list_xmlroot = XmlElementTree.fromstring(content)
2294
2295 network_configuration['status'] = vm_list_xmlroot.get("status")
2296 network_configuration['name'] = vm_list_xmlroot.get("name")
2297 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2298
2299 for child in vm_list_xmlroot:
2300 if child.tag.split("}")[1] == 'IsShared':
2301 network_configuration['isShared'] = child.text.strip()
2302 if child.tag.split("}")[1] == 'Configuration':
2303 for configuration in child.iter():
2304 tagKey = configuration.tag.split("}")[1].strip()
2305 if tagKey != "":
2306 network_configuration[tagKey] = configuration.text.strip()
2307 return network_configuration
2308 except:
2309 pass
2310
2311 return network_configuration
2312
2313 def delete_network_action(self, network_uuid=None):
2314 """
2315 Method delete given network from vCloud director
2316
2317 Args:
2318 network_uuid - is a network uuid that client wish to delete
2319
2320 Returns:
2321 The return None or XML respond or false
2322 """
2323
2324 vca = self.connect_as_admin()
2325 if not vca:
2326 raise vimconn.vimconnConnectionException("self.connect() is failed")
2327 if network_uuid is None:
2328 return False
2329
2330 url_list = [vca.host, '/api/admin/network/', network_uuid]
2331 vm_list_rest_call = ''.join(url_list)
2332
2333 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2334 response = Http.delete(url=vm_list_rest_call,
2335 headers=vca.vcloud_session.get_vcloud_headers(),
2336 verify=vca.verify,
2337 logger=vca.logger)
2338
2339 if response.status_code == 202:
2340 return True
2341
2342 return False
2343
bhangare0e571a92017-01-12 04:02:23 -08002344 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2345 ip_profile=None, isshared='true'):
bayramovef390722016-09-27 03:34:46 -07002346 """
2347 Method create network in vCloud director
2348
2349 Args:
2350 network_name - is network name to be created.
bhangare0e571a92017-01-12 04:02:23 -08002351 net_type - can be 'bridge','data','ptp','mgmt'.
2352 ip_profile is a dict containing the IP parameters of the network
2353 isshared - is a boolean
bayramovef390722016-09-27 03:34:46 -07002354 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2355 It optional attribute. by default if no parent network indicate the first available will be used.
2356
2357 Returns:
2358 The return network uuid or return None
2359 """
2360
bayramov5761ad12016-10-04 09:00:30 +04002361 new_network_name = [network_name, '-', str(uuid.uuid4())]
2362 content = self.create_network_rest(network_name=''.join(new_network_name),
bhangare0e571a92017-01-12 04:02:23 -08002363 ip_profile=ip_profile,
2364 net_type=net_type,
bayramovef390722016-09-27 03:34:46 -07002365 parent_network_uuid=parent_network_uuid,
2366 isshared=isshared)
2367 if content is None:
2368 self.logger.debug("Failed create network {}.".format(network_name))
2369 return None
2370
2371 try:
2372 vm_list_xmlroot = XmlElementTree.fromstring(content)
2373 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2374 if len(vcd_uuid) == 4:
2375 self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2376 return vcd_uuid[3]
2377 except:
2378 self.logger.debug("Failed create network {}".format(network_name))
2379 return None
2380
bhangare0e571a92017-01-12 04:02:23 -08002381 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2382 ip_profile=None, isshared='true'):
bayramovef390722016-09-27 03:34:46 -07002383 """
2384 Method create network in vCloud director
2385
2386 Args:
2387 network_name - is network name to be created.
bhangare0e571a92017-01-12 04:02:23 -08002388 net_type - can be 'bridge','data','ptp','mgmt'.
2389 ip_profile is a dict containing the IP parameters of the network
2390 isshared - is a boolean
bayramovef390722016-09-27 03:34:46 -07002391 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2392 It optional attribute. by default if no parent network indicate the first available will be used.
2393
2394 Returns:
2395 The return network uuid or return None
2396 """
2397
2398 vca = self.connect_as_admin()
2399 if not vca:
bayramov5761ad12016-10-04 09:00:30 +04002400 raise vimconn.vimconnConnectionException("self.connect() is failed.")
bayramovef390722016-09-27 03:34:46 -07002401 if network_name is None:
2402 return None
2403
2404 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2405 vm_list_rest_call = ''.join(url_list)
2406 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2407 response = Http.get(url=vm_list_rest_call,
2408 headers=vca.vcloud_session.get_vcloud_headers(),
2409 verify=vca.verify,
2410 logger=vca.logger)
2411
2412 provider_network = None
2413 available_networks = None
2414 add_vdc_rest_url = None
2415
2416 if response.status_code != requests.codes.ok:
2417 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2418 response.status_code))
2419 return None
2420 else:
2421 try:
2422 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2423 for child in vm_list_xmlroot:
2424 if child.tag.split("}")[1] == 'ProviderVdcReference':
2425 provider_network = child.attrib.get('href')
2426 # application/vnd.vmware.admin.providervdc+xml
2427 if child.tag.split("}")[1] == 'Link':
2428 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2429 and child.attrib.get('rel') == 'add':
2430 add_vdc_rest_url = child.attrib.get('href')
2431 except:
2432 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2433 self.logger.debug("Respond body {}".format(response.content))
2434 return None
2435
2436 # find pvdc provided available network
2437 response = Http.get(url=provider_network,
2438 headers=vca.vcloud_session.get_vcloud_headers(),
2439 verify=vca.verify,
2440 logger=vca.logger)
2441 if response.status_code != requests.codes.ok:
2442 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2443 response.status_code))
2444 return None
2445
2446 # available_networks.split("/")[-1]
2447
2448 if parent_network_uuid is None:
2449 try:
2450 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2451 for child in vm_list_xmlroot.iter():
2452 if child.tag.split("}")[1] == 'AvailableNetworks':
2453 for networks in child.iter():
2454 # application/vnd.vmware.admin.network+xml
2455 if networks.attrib.get('href') is not None:
2456 available_networks = networks.attrib.get('href')
2457 break
2458 except:
2459 return None
2460
bhangare0e571a92017-01-12 04:02:23 -08002461 #Configure IP profile of the network
2462 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2463
2464 gateway_address=ip_profile['gateway_address']
2465 dhcp_count=int(ip_profile['dhcp_count'])
2466 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2467
2468 if ip_profile['dhcp_enabled']==True:
2469 dhcp_enabled='true'
2470 else:
2471 dhcp_enabled='false'
2472 dhcp_start_address=ip_profile['dhcp_start_address']
2473
2474 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2475 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2476 end_ip_int += dhcp_count - 1
2477 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2478
2479 ip_version=ip_profile['ip_version']
2480 dns_address=ip_profile['dns_address']
2481
bayramovef390722016-09-27 03:34:46 -07002482 # either use client provided UUID or search for a first available
2483 # if both are not defined we return none
2484 if parent_network_uuid is not None:
2485 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2486 add_vdc_rest_url = ''.join(url_list)
2487
bhangare0e571a92017-01-12 04:02:23 -08002488 if net_type=='ptp':
2489 fence_mode="isolated"
2490 isshared='false'
2491 is_inherited='false'
2492 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2493 <Description>Openmano created</Description>
2494 <Configuration>
2495 <IpScopes>
2496 <IpScope>
2497 <IsInherited>{1:s}</IsInherited>
2498 <Gateway>{2:s}</Gateway>
2499 <Netmask>{3:s}</Netmask>
2500 <Dns1>{4:s}</Dns1>
2501 <IsEnabled>{5:s}</IsEnabled>
2502 <IpRanges>
2503 <IpRange>
2504 <StartAddress>{6:s}</StartAddress>
2505 <EndAddress>{7:s}</EndAddress>
2506 </IpRange>
2507 </IpRanges>
2508 </IpScope>
2509 </IpScopes>
2510 <FenceMode>{8:s}</FenceMode>
2511 </Configuration>
2512 <IsShared>{9:s}</IsShared>
2513 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2514 subnet_address, dns_address, dhcp_enabled,
2515 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2516
2517 else:
2518 fence_mode="bridged"
2519 is_inherited='false'
2520 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2521 <Description>Openmano created</Description>
2522 <Configuration>
2523 <IpScopes>
2524 <IpScope>
2525 <IsInherited>{1:s}</IsInherited>
2526 <Gateway>{2:s}</Gateway>
2527 <Netmask>{3:s}</Netmask>
2528 <Dns1>{4:s}</Dns1>
2529 <IsEnabled>{5:s}</IsEnabled>
2530 <IpRanges>
2531 <IpRange>
2532 <StartAddress>{6:s}</StartAddress>
2533 <EndAddress>{7:s}</EndAddress>
2534 </IpRange>
2535 </IpRanges>
2536 </IpScope>
2537 </IpScopes>
2538 <ParentNetwork href="{8:s}"/>
2539 <FenceMode>{9:s}</FenceMode>
2540 </Configuration>
2541 <IsShared>{10:s}</IsShared>
2542 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2543 subnet_address, dns_address, dhcp_enabled,
2544 dhcp_start_address, dhcp_end_address, available_networks,
2545 fence_mode, isshared)
bayramovef390722016-09-27 03:34:46 -07002546
2547 headers = vca.vcloud_session.get_vcloud_headers()
2548 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
bhangare0e571a92017-01-12 04:02:23 -08002549 try:
2550 response = Http.post(url=add_vdc_rest_url,
2551 headers=headers,
2552 data=data,
2553 verify=vca.verify,
2554 logger=vca.logger)
bayramovef390722016-09-27 03:34:46 -07002555
bhangare0e571a92017-01-12 04:02:23 -08002556 if response.status_code != 201:
2557 self.logger.debug("Create Network POST REST API call failed. Return status code {}"
2558 .format(response.status_code))
2559 else:
2560 network = networkType.parseString(response.content, True)
2561 create_nw_task = network.get_Tasks().get_Task()[0]
2562
2563 # if we all ok we respond with content after network creation completes
2564 # otherwise by default return None
2565 if create_nw_task is not None:
2566 self.logger.debug("Create Network REST : Waiting for Nw creation complete")
2567 status = vca.block_until_completed(create_nw_task)
2568 if status:
2569 return response.content
2570 else:
2571 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2572 .format(response.content))
2573 except Exception as exp:
2574 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2575
2576 return None
2577
2578 def convert_cidr_to_netmask(self, cidr_ip=None):
2579 """
2580 Method sets convert CIDR netmask address to normal IP format
2581 Args:
2582 cidr_ip : CIDR IP address
2583 Returns:
2584 netmask : Converted netmask
2585 """
2586 if cidr_ip is not None:
2587 if '/' in cidr_ip:
2588 network, net_bits = cidr_ip.split('/')
2589 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2590 else:
2591 netmask = cidr_ip
2592 return netmask
bayramovef390722016-09-27 03:34:46 -07002593 return None
2594
2595 def get_provider_rest(self, vca=None):
2596 """
2597 Method gets provider vdc view from vcloud director
2598
2599 Args:
2600 network_name - is network name to be created.
2601 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2602 It optional attribute. by default if no parent network indicate the first available will be used.
2603
2604 Returns:
2605 The return xml content of respond or None
2606 """
2607
2608 url_list = [vca.host, '/api/admin']
2609 response = Http.get(url=''.join(url_list),
2610 headers=vca.vcloud_session.get_vcloud_headers(),
2611 verify=vca.verify,
2612 logger=vca.logger)
2613
2614 if response.status_code == requests.codes.ok:
2615 return response.content
2616 return None
2617
2618 def create_vdc(self, vdc_name=None):
2619
2620 vdc_dict = {}
2621
2622 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2623 if xml_content is not None:
bayramovef390722016-09-27 03:34:46 -07002624 try:
2625 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2626 for child in task_resp_xmlroot:
2627 if child.tag.split("}")[1] == 'Owner':
2628 vdc_id = child.attrib.get('href').split("/")[-1]
2629 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2630 return vdc_dict
2631 except:
2632 self.logger.debug("Respond body {}".format(xml_content))
2633
2634 return None
2635
2636 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2637 """
2638 Method create vdc in vCloud director based on VDC template.
2639 it uses pre-defined template that must be named openmano
2640
2641 Args:
2642 vdc_name - name of a new vdc.
2643
2644 Returns:
2645 The return xml content of respond or None
2646 """
2647
2648 self.logger.info("Creating new vdc {}".format(vdc_name))
bayramovef390722016-09-27 03:34:46 -07002649 vca = self.connect()
2650 if not vca:
2651 raise vimconn.vimconnConnectionException("self.connect() is failed")
2652 if vdc_name is None:
2653 return None
2654
2655 url_list = [vca.host, '/api/vdcTemplates']
2656 vm_list_rest_call = ''.join(url_list)
2657 response = Http.get(url=vm_list_rest_call,
2658 headers=vca.vcloud_session.get_vcloud_headers(),
2659 verify=vca.verify,
2660 logger=vca.logger)
2661
2662 # container url to a template
2663 vdc_template_ref = None
2664 try:
2665 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2666 for child in vm_list_xmlroot:
2667 # application/vnd.vmware.admin.providervdc+xml
2668 # we need find a template from witch we instantiate VDC
2669 if child.tag.split("}")[1] == 'VdcTemplate':
2670 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2671 'name') == 'openmano':
2672 vdc_template_ref = child.attrib.get('href')
2673 except:
2674 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2675 self.logger.debug("Respond body {}".format(response.content))
2676 return None
2677
2678 # if we didn't found required pre defined template we return None
2679 if vdc_template_ref is None:
2680 return None
2681
2682 try:
2683 # instantiate vdc
2684 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2685 vm_list_rest_call = ''.join(url_list)
2686 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2687 <Source href="{1:s}"></Source>
2688 <Description>opnemano</Description>
2689 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2690 headers = vca.vcloud_session.get_vcloud_headers()
2691 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2692 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2693 logger=vca.logger)
2694 # if we all ok we respond with content otherwise by default None
2695 if response.status_code >= 200 and response.status_code < 300:
2696 return response.content
2697 return None
2698 except:
2699 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2700 self.logger.debug("Respond body {}".format(response.content))
2701
2702 return None
2703
2704 def create_vdc_rest(self, vdc_name=None):
2705 """
2706 Method create network in vCloud director
2707
2708 Args:
2709 network_name - is network name to be created.
2710 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2711 It optional attribute. by default if no parent network indicate the first available will be used.
2712
2713 Returns:
2714 The return network uuid or return None
2715 """
2716
2717 self.logger.info("Creating new vdc {}".format(vdc_name))
bayramovef390722016-09-27 03:34:46 -07002718
2719 vca = self.connect_as_admin()
2720 if not vca:
2721 raise vimconn.vimconnConnectionException("self.connect() is failed")
2722 if vdc_name is None:
2723 return None
2724
2725 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2726 vm_list_rest_call = ''.join(url_list)
2727 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2728 response = Http.get(url=vm_list_rest_call,
2729 headers=vca.vcloud_session.get_vcloud_headers(),
2730 verify=vca.verify,
2731 logger=vca.logger)
2732
2733 provider_vdc_ref = None
2734 add_vdc_rest_url = None
2735 available_networks = None
2736
2737 if response.status_code != requests.codes.ok:
2738 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2739 response.status_code))
2740 return None
2741 else:
2742 try:
2743 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2744 for child in vm_list_xmlroot:
2745 # application/vnd.vmware.admin.providervdc+xml
2746 if child.tag.split("}")[1] == 'Link':
2747 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2748 and child.attrib.get('rel') == 'add':
2749 add_vdc_rest_url = child.attrib.get('href')
2750 except:
2751 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2752 self.logger.debug("Respond body {}".format(response.content))
2753 return None
2754
2755 response = self.get_provider_rest(vca=vca)
bayramovef390722016-09-27 03:34:46 -07002756 try:
2757 vm_list_xmlroot = XmlElementTree.fromstring(response)
2758 for child in vm_list_xmlroot:
2759 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2760 for sub_child in child:
2761 provider_vdc_ref = sub_child.attrib.get('href')
2762 except:
2763 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2764 self.logger.debug("Respond body {}".format(response))
2765 return None
2766
bayramovef390722016-09-27 03:34:46 -07002767 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2768 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2769 <AllocationModel>ReservationPool</AllocationModel>
2770 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2771 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2772 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2773 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2774 <ProviderVdcReference
2775 name="Main Provider"
2776 href="{2:s}" />
2777 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2778 escape(vdc_name),
2779 provider_vdc_ref)
2780
bayramovef390722016-09-27 03:34:46 -07002781 headers = vca.vcloud_session.get_vcloud_headers()
2782 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2783 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2784 logger=vca.logger)
2785
bayramovef390722016-09-27 03:34:46 -07002786 # if we all ok we respond with content otherwise by default None
2787 if response.status_code == 201:
2788 return response.content
2789 return None
bayramovfe3f3c92016-10-04 07:53:41 +04002790
bhangarefda5f7c2017-01-12 23:50:34 -08002791 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
bayramovfe3f3c92016-10-04 07:53:41 +04002792 """
2793 Method retrieve vapp detail from vCloud director
2794
2795 Args:
2796 vapp_uuid - is vapp identifier.
2797
2798 Returns:
2799 The return network uuid or return None
2800 """
2801
2802 parsed_respond = {}
bhangarefda5f7c2017-01-12 23:50:34 -08002803 vca = None
bayramovfe3f3c92016-10-04 07:53:41 +04002804
bhangarefda5f7c2017-01-12 23:50:34 -08002805 if need_admin_access:
2806 vca = self.connect_as_admin()
2807 else:
2808 vca = self.connect()
2809
bayramovfe3f3c92016-10-04 07:53:41 +04002810 if not vca:
2811 raise vimconn.vimconnConnectionException("self.connect() is failed")
2812 if vapp_uuid is None:
2813 return None
2814
2815 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2816 get_vapp_restcall = ''.join(url_list)
bhangarefda5f7c2017-01-12 23:50:34 -08002817
2818 if vca.vcloud_session and vca.vcloud_session.organization:
bayramovfe3f3c92016-10-04 07:53:41 +04002819 response = Http.get(url=get_vapp_restcall,
2820 headers=vca.vcloud_session.get_vcloud_headers(),
2821 verify=vca.verify,
2822 logger=vca.logger)
2823
2824 if response.status_code != requests.codes.ok:
2825 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2826 response.status_code))
2827 return parsed_respond
2828
2829 try:
2830 xmlroot_respond = XmlElementTree.fromstring(response.content)
2831 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
2832
bhangarea92ae392017-01-12 22:30:29 -08002833 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2834 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2835 'vmw': 'http://www.vmware.com/schema/ovf',
2836 'vm': 'http://www.vmware.com/vcloud/v1.5',
2837 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2838 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2839 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2840 }
bayramovfe3f3c92016-10-04 07:53:41 +04002841
bhangarea92ae392017-01-12 22:30:29 -08002842 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
bayramovfe3f3c92016-10-04 07:53:41 +04002843 if created_section is not None:
2844 parsed_respond['created'] = created_section.text
2845
bhangarea92ae392017-01-12 22:30:29 -08002846 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
bayramov5761ad12016-10-04 09:00:30 +04002847 if network_section is not None and 'networkName' in network_section.attrib:
bayramovfe3f3c92016-10-04 07:53:41 +04002848 parsed_respond['networkname'] = network_section.attrib['networkName']
2849
2850 ipscopes_section = \
2851 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
bhangarea92ae392017-01-12 22:30:29 -08002852 namespaces)
bayramovfe3f3c92016-10-04 07:53:41 +04002853 if ipscopes_section is not None:
2854 for ipscope in ipscopes_section:
2855 for scope in ipscope:
2856 tag_key = scope.tag.split("}")[1]
2857 if tag_key == 'IpRanges':
2858 ip_ranges = scope.getchildren()
2859 for ipblock in ip_ranges:
2860 for block in ipblock:
2861 parsed_respond[block.tag.split("}")[1]] = block.text
2862 else:
2863 parsed_respond[tag_key] = scope.text
2864
2865 # parse children section for other attrib
bhangarea92ae392017-01-12 22:30:29 -08002866 children_section = xmlroot_respond.find('vm:Children/', namespaces)
bayramovfe3f3c92016-10-04 07:53:41 +04002867 if children_section is not None:
2868 parsed_respond['name'] = children_section.attrib['name']
bhangarea92ae392017-01-12 22:30:29 -08002869 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
2870 if "nestedHypervisorEnabled" in children_section.attrib else None
bayramovfe3f3c92016-10-04 07:53:41 +04002871 parsed_respond['deployed'] = children_section.attrib['deployed']
2872 parsed_respond['status'] = children_section.attrib['status']
2873 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
bhangarea92ae392017-01-12 22:30:29 -08002874 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
bayramovfe3f3c92016-10-04 07:53:41 +04002875 nic_list = []
2876 for adapters in network_adapter:
2877 adapter_key = adapters.tag.split("}")[1]
2878 if adapter_key == 'PrimaryNetworkConnectionIndex':
2879 parsed_respond['primarynetwork'] = adapters.text
2880 if adapter_key == 'NetworkConnection':
2881 vnic = {}
bayramov5761ad12016-10-04 09:00:30 +04002882 if 'network' in adapters.attrib:
bayramovfe3f3c92016-10-04 07:53:41 +04002883 vnic['network'] = adapters.attrib['network']
2884 for adapter in adapters:
2885 setting_key = adapter.tag.split("}")[1]
2886 vnic[setting_key] = adapter.text
2887 nic_list.append(vnic)
2888
2889 for link in children_section:
bayramov5761ad12016-10-04 09:00:30 +04002890 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
bayramovfe3f3c92016-10-04 07:53:41 +04002891 if link.attrib['rel'] == 'screen:acquireTicket':
2892 parsed_respond['acquireTicket'] = link.attrib
2893 if link.attrib['rel'] == 'screen:acquireMksTicket':
2894 parsed_respond['acquireMksTicket'] = link.attrib
2895
2896 parsed_respond['interfaces'] = nic_list
bhangarefda5f7c2017-01-12 23:50:34 -08002897 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
2898 if vCloud_extension_section is not None:
2899 vm_vcenter_info = {}
2900 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
2901 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
2902 if vmext is not None:
2903 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
2904 vm_vcenter_info["vim_server_href"] = vmext.find('vmext:VimServerRef', namespaces).attrib['href']
2905 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
bayramovfe3f3c92016-10-04 07:53:41 +04002906
bhangarea92ae392017-01-12 22:30:29 -08002907 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
2908 vm_virtual_hardware_info = {}
2909 if virtual_hardware_section is not None:
2910 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
2911 if item.find("rasd:Description",namespaces).text == "Hard disk":
2912 disk_size = item.find("rasd:HostResource" ,namespaces
2913 ).attrib["{"+namespaces['vm']+"}capacity"]
2914
2915 vm_virtual_hardware_info["disk_size"]= disk_size
2916 break
2917
2918 for link in virtual_hardware_section:
2919 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2920 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
2921 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
2922 break
2923
2924 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
2925 except Exception as exp :
2926 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
bayramovfe3f3c92016-10-04 07:53:41 +04002927 return parsed_respond
2928
bayramov5761ad12016-10-04 09:00:30 +04002929 def acuire_console(self, vm_uuid=None):
bayramovfe3f3c92016-10-04 07:53:41 +04002930
2931 vca = self.connect()
2932 if not vca:
2933 raise vimconn.vimconnConnectionException("self.connect() is failed")
2934 if vm_uuid is None:
2935 return None
2936
2937 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
bayramov5761ad12016-10-04 09:00:30 +04002938 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
bayramovfe3f3c92016-10-04 07:53:41 +04002939 console_dict = vm_dict['acquireTicket']
2940 console_rest_call = console_dict['href']
2941
2942 response = Http.post(url=console_rest_call,
2943 headers=vca.vcloud_session.get_vcloud_headers(),
2944 verify=vca.verify,
2945 logger=vca.logger)
2946
bayramov5761ad12016-10-04 09:00:30 +04002947 if response.status_code == requests.codes.ok:
2948 return response.content
bayramovfe3f3c92016-10-04 07:53:41 +04002949
kate15f1c382016-12-15 01:12:40 -08002950 return None
kate13ab2c42016-12-23 01:34:24 -08002951
bhangarea92ae392017-01-12 22:30:29 -08002952 def modify_vm_disk(self, vapp_uuid, flavor_disk):
2953 """
2954 Method retrieve vm disk details
2955
2956 Args:
2957 vapp_uuid - is vapp identifier.
2958 flavor_disk - disk size as specified in VNFD (flavor)
2959
2960 Returns:
2961 The return network uuid or return None
2962 """
2963 status = None
2964 try:
2965 #Flavor disk is in GB convert it into MB
2966 flavor_disk = int(flavor_disk) * 1024
2967 vm_details = self.get_vapp_details_rest(vapp_uuid)
2968 if vm_details:
2969 vm_name = vm_details["name"]
2970 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
2971
2972 if vm_details and "vm_virtual_hardware" in vm_details:
2973 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
2974 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
2975
2976 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
2977
2978 if flavor_disk > vm_disk:
2979 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
2980 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
2981 vm_disk, flavor_disk ))
2982 else:
2983 status = True
2984 self.logger.info("No need to modify disk of VM {}".format(vm_name))
2985
2986 return status
2987 except Exception as exp:
2988 self.logger.info("Error occurred while modifing disk size {}".format(exp))
2989
2990
2991 def modify_vm_disk_rest(self, disk_href , disk_size):
2992 """
2993 Method retrieve modify vm disk size
2994
2995 Args:
2996 disk_href - vCD API URL to GET and PUT disk data
2997 disk_size - disk size as specified in VNFD (flavor)
2998
2999 Returns:
3000 The return network uuid or return None
3001 """
3002 vca = self.connect()
3003 if not vca:
3004 raise vimconn.vimconnConnectionException("self.connect() is failed")
3005 if disk_href is None or disk_size is None:
3006 return None
3007
3008 if vca.vcloud_session and vca.vcloud_session.organization:
3009 response = Http.get(url=disk_href,
3010 headers=vca.vcloud_session.get_vcloud_headers(),
3011 verify=vca.verify,
3012 logger=vca.logger)
3013
3014 if response.status_code != requests.codes.ok:
3015 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
3016 response.status_code))
3017 return None
3018 try:
3019 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
3020 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
3021 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3022
3023 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
3024 if item.find("rasd:Description",namespaces).text == "Hard disk":
3025 disk_item = item.find("rasd:HostResource" ,namespaces )
3026 if disk_item is not None:
3027 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
3028 break
3029
3030 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
3031 xml_declaration=True)
3032
3033 #Send PUT request to modify disk size
3034 headers = vca.vcloud_session.get_vcloud_headers()
3035 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3036
3037 response = Http.put(url=disk_href,
3038 data=data,
3039 headers=headers,
3040 verify=vca.verify, logger=self.logger)
3041
3042 if response.status_code != 202:
3043 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
3044 response.status_code))
3045 else:
3046 modify_disk_task = taskType.parseString(response.content, True)
3047 if type(modify_disk_task) is GenericTask:
3048 status = vca.block_until_completed(modify_disk_task)
3049 return status
3050
3051 return None
3052
3053 except Exception as exp :
3054 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
3055 return None
3056
bhangarefda5f7c2017-01-12 23:50:34 -08003057 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
3058 """
3059 Method to attach pci devices to VM
3060
3061 Args:
3062 vapp_uuid - uuid of vApp/VM
3063 pci_devices - pci devices infromation as specified in VNFD (flavor)
3064
3065 Returns:
3066 The status of add pci device task , vm object and
3067 vcenter_conect object
3068 """
3069 vm_obj = None
3070 vcenter_conect = None
3071 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3072 #Assuming password of vCenter user is same as password of vCloud user
3073 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vapp_uuid)
3074 self.logger.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
3075 "vm_vcenter_port{}".format(
3076 vm_moref_id, vm_vcenter_host,
3077 vm_vcenter_username, vm_vcenter_port))
3078 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3079 context = None
3080 if hasattr(ssl, '_create_unverified_context'):
3081 context = ssl._create_unverified_context()
3082 try:
3083 no_of_pci_devices = len(pci_devices)
3084 if no_of_pci_devices > 0:
3085 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3086 pwd=self.passwd, port=int(vm_vcenter_port) ,
3087 sslContext=context)
3088 atexit.register(Disconnect, vcenter_conect)
3089 content = vcenter_conect.RetrieveContent()
3090
3091 #Get VM and its host
3092 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3093 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3094 if host_obj and vm_obj:
3095 #get PCI devies from host on which vapp is currently installed
3096 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3097
3098 if avilable_pci_devices is None:
3099 #find other hosts with active pci devices
3100 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3101 content,
3102 no_of_pci_devices
3103 )
3104
3105 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3106 #Migrate vm to the host where PCI devices are availble
3107 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3108 task = self.relocate_vm(new_host_obj, vm_obj)
3109 if task is not None:
3110 result = self.wait_for_vcenter_task(task, vcenter_conect)
3111 self.logger.info("Migrate VM status: {}".format(result))
3112 host_obj = new_host_obj
3113 else:
3114 self.logger.info("Fail to migrate VM : {}".format(result))
3115 raise vimconn.vimconnNotFoundException(
3116 "Fail to migrate VM : {} to host {}".format(
3117 vmname_andid,
3118 new_host_obj)
3119 )
3120
3121 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3122 #Add PCI devices one by one
3123 for pci_device in avilable_pci_devices:
3124 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3125 if task:
3126 status= self.wait_for_vcenter_task(task, vcenter_conect)
3127 if status:
3128 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3129 else:
3130 self.logger.info("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3131 return True, vm_obj, vcenter_conect
3132 else:
3133 self.logger.error("Currently there is no host with"\
3134 " {} number of avaialble PCI devices required for VM {}".format(
3135 no_of_pci_devices,
3136 vmname_andid)
3137 )
3138 raise vimconn.vimconnNotFoundException(
3139 "Currently there is no host with {} "\
3140 "number of avaialble PCI devices required for VM {}".format(
3141 no_of_pci_devices,
3142 vmname_andid))
3143 else:
3144 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3145
3146 except vmodl.MethodFault as error:
3147 self.logger.error("Error occurred while adding PCI devices {} ",error)
3148 return None, vm_obj, vcenter_conect
3149
3150 def get_vm_obj(self, content, mob_id):
3151 """
3152 Method to get the vsphere VM object associated with a given morf ID
3153 Args:
3154 vapp_uuid - uuid of vApp/VM
3155 content - vCenter content object
3156 mob_id - mob_id of VM
3157
3158 Returns:
3159 VM and host object
3160 """
3161 vm_obj = None
3162 host_obj = None
3163 try :
3164 container = content.viewManager.CreateContainerView(content.rootFolder,
3165 [vim.VirtualMachine], True
3166 )
3167 for vm in container.view:
3168 mobID = vm._GetMoId()
3169 if mobID == mob_id:
3170 vm_obj = vm
3171 host_obj = vm_obj.runtime.host
3172 break
3173 except Exception as exp:
3174 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3175 return host_obj, vm_obj
3176
3177 def get_pci_devices(self, host, need_devices):
3178 """
3179 Method to get the details of pci devices on given host
3180 Args:
3181 host - vSphere host object
3182 need_devices - number of pci devices needed on host
3183
3184 Returns:
3185 array of pci devices
3186 """
3187 all_devices = []
3188 all_device_ids = []
3189 used_devices_ids = []
3190
3191 try:
3192 if host:
3193 pciPassthruInfo = host.config.pciPassthruInfo
3194 pciDevies = host.hardware.pciDevice
3195
3196 for pci_status in pciPassthruInfo:
3197 if pci_status.passthruActive:
3198 for device in pciDevies:
3199 if device.id == pci_status.id:
3200 all_device_ids.append(device.id)
3201 all_devices.append(device)
3202
3203 #check if devices are in use
3204 avalible_devices = all_devices
3205 for vm in host.vm:
3206 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3207 vm_devices = vm.config.hardware.device
3208 for device in vm_devices:
3209 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3210 if device.backing.id in all_device_ids:
3211 for use_device in avalible_devices:
3212 if use_device.id == device.backing.id:
3213 avalible_devices.remove(use_device)
3214 used_devices_ids.append(device.backing.id)
3215 self.logger.debug("Device {} from devices {}"\
3216 "is in use".format(device.backing.id,
3217 device)
3218 )
3219 if len(avalible_devices) < need_devices:
3220 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3221 need_devices))
3222 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3223 avalible_devices))
3224 return None
3225 else:
3226 required_devices = avalible_devices[:need_devices]
3227 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3228 len(avalible_devices),
3229 host,
3230 need_devices))
3231 self.logger.info("Retruning {} devices as {}".format(need_devices,
3232 required_devices ))
3233 return required_devices
3234
3235 except Exception as exp:
3236 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3237
3238 return None
3239
3240 def get_host_and_PCIdevices(self, content, need_devices):
3241 """
3242 Method to get the details of pci devices infromation on all hosts
3243
3244 Args:
3245 content - vSphere host object
3246 need_devices - number of pci devices needed on host
3247
3248 Returns:
3249 array of pci devices and host object
3250 """
3251 host_obj = None
3252 pci_device_objs = None
3253 try:
3254 if content:
3255 container = content.viewManager.CreateContainerView(content.rootFolder,
3256 [vim.HostSystem], True)
3257 for host in container.view:
3258 devices = self.get_pci_devices(host, need_devices)
3259 if devices:
3260 host_obj = host
3261 pci_device_objs = devices
3262 break
3263 except Exception as exp:
3264 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3265
3266 return host_obj,pci_device_objs
3267
3268 def relocate_vm(self, dest_host, vm) :
3269 """
3270 Method to get the relocate VM to new host
3271
3272 Args:
3273 dest_host - vSphere host object
3274 vm - vSphere VM object
3275
3276 Returns:
3277 task object
3278 """
3279 task = None
3280 try:
3281 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3282 task = vm.Relocate(relocate_spec)
3283 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3284 except Exception as exp:
3285 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3286 dest_host, vm, exp))
3287 return task
3288
3289 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3290 """
3291 Waits and provides updates on a vSphere task
3292 """
3293 while task.info.state == vim.TaskInfo.State.running:
3294 time.sleep(2)
3295
3296 if task.info.state == vim.TaskInfo.State.success:
3297 if task.info.result is not None and not hideResult:
3298 self.logger.info('{} completed successfully, result: {}'.format(
3299 actionName,
3300 task.info.result))
3301 else:
3302 self.logger.info('Task {} completed successfully.'.format(actionName))
3303 else:
3304 self.logger.error('{} did not complete successfully: {} '.format(
3305 actionName,
3306 task.info.error)
3307 )
3308
3309 return task.info.result
3310
3311 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3312 """
3313 Method to add pci device in given VM
3314
3315 Args:
3316 host_object - vSphere host object
3317 vm_object - vSphere VM object
3318 host_pci_dev - host_pci_dev must be one of the devices from the
3319 host_object.hardware.pciDevice list
3320 which is configured as a PCI passthrough device
3321
3322 Returns:
3323 task object
3324 """
3325 task = None
3326 if vm_object and host_object and host_pci_dev:
3327 try :
3328 #Add PCI device to VM
3329 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3330 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3331
3332 if host_pci_dev.id not in systemid_by_pciid:
3333 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3334 return None
3335
3336 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3337 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3338 id=host_pci_dev.id,
3339 systemId=systemid_by_pciid[host_pci_dev.id],
3340 vendorId=host_pci_dev.vendorId,
3341 deviceName=host_pci_dev.deviceName)
3342
3343 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3344
3345 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3346 new_device_config.operation = "add"
3347 vmConfigSpec = vim.vm.ConfigSpec()
3348 vmConfigSpec.deviceChange = [new_device_config]
3349
3350 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3351 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3352 host_pci_dev, vm_object, host_object)
3353 )
3354 except Exception as exp:
3355 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3356 host_pci_dev,
3357 vm_object,
3358 exp))
3359 return task
3360
3361 def get_vcenter_info_rest(self , vapp_uuid):
3362 """
3363 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3364 Method to get details of vCenter
3365
3366 Args:
3367 vapp_uuid - uuid of vApp or VM
3368
3369 Returns:
3370 Moref Id of VM and deails of vCenter
3371 """
3372 vm_moref_id = None
3373 vm_vcenter = None
3374 vm_vcenter_username = None
3375 vm_vcenter_port = None
3376
3377 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
3378 if vm_details and "vm_vcenter_info" in vm_details:
3379 vm_moref_id = vm_details["vm_vcenter_info"]["vm_moref_id"]
3380 vim_server_href = vm_details["vm_vcenter_info"]["vim_server_href"]
3381
3382 if vim_server_href:
3383 vca = self.connect_as_admin()
3384 if not vca:
3385 raise vimconn.vimconnConnectionException("self.connect() is failed")
3386 if vim_server_href is None:
3387 self.logger.error("No url to get vcenter details")
3388
3389 if vca.vcloud_session and vca.vcloud_session.organization:
3390 response = Http.get(url=vim_server_href,
3391 headers=vca.vcloud_session.get_vcloud_headers(),
3392 verify=vca.verify,
3393 logger=vca.logger)
3394
3395 if response.status_code != requests.codes.ok:
3396 self.logger.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href,
3397 response.status_code))
3398 try:
3399 namespaces={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3400 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3401 }
3402 xmlroot_respond = XmlElementTree.fromstring(response.content)
3403 vm_vcenter_username = xmlroot_respond.find('vmext:Username', namespaces).text
3404 vcenter_url = xmlroot_respond.find('vmext:Url', namespaces).text
3405 vm_vcenter_port = vcenter_url.split(":")[2]
3406 vm_vcenter = vcenter_url.split(":")[1].split("//")[1]
3407
3408 except Exception as exp :
3409 self.logger.info("Error occurred calling rest api for vcenter information {}".format(exp))
3410
3411 return vm_moref_id , vm_vcenter , vm_vcenter_username, vm_vcenter_port
3412
3413
3414 def get_vm_pci_details(self, vmuuid):
3415 """
3416 Method to get VM PCI device details from vCenter
3417
3418 Args:
3419 vm_obj - vSphere VM object
3420
3421 Returns:
3422 dict of PCI devives attached to VM
3423
3424 """
3425 vm_pci_devices_info = {}
3426 try:
3427 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
3428 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3429 context = None
3430 if hasattr(ssl, '_create_unverified_context'):
3431 context = ssl._create_unverified_context()
3432 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3433 pwd=self.passwd, port=int(vm_vcenter_port),
3434 sslContext=context)
3435 atexit.register(Disconnect, vcenter_conect)
3436 content = vcenter_conect.RetrieveContent()
3437
3438 #Get VM and its host
3439 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3440 for device in vm_obj.config.hardware.device:
3441 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3442 device_details={'devide_id':device.backing.id,
3443 'pciSlotNumber':device.slotInfo.pciSlotNumber
3444 }
3445 vm_pci_devices_info[device.deviceInfo.label] = device_details
3446 except Exception as exp:
3447 self.logger.info("Error occurred while getting PCI devices infromationn"\
3448 " for VM {} : {}".format(vm_obj,exp))
3449 return vm_pci_devices_info
3450
bhangare0e571a92017-01-12 04:02:23 -08003451