05b0714b4b
Change-Id: Icfbf617873fa10f209fd498f439f744fc5878926
330 lines
13 KiB
Python
330 lines
13 KiB
Python
# Copyright (c) 2014 Marist SDN Innovation lab Joint with Plexxi Inc.
|
|
# All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
|
|
from __future__ import print_function
|
|
from __future__ import division
|
|
from __future__ import absolute_import
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_vmware import api
|
|
from oslo_vmware import vim_util
|
|
|
|
from congress.datasources import constants
|
|
from congress.datasources import datasource_driver
|
|
from congress.datasources import datasource_utils as ds_utils
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class VCenterDriver(datasource_driver.PollingDataSourceDriver,
|
|
datasource_driver.ExecutionDriver):
|
|
|
|
HOSTS = "hosts"
|
|
HOST_DNS = "host.DNS_IPs"
|
|
HOST_PNICS = "host.PNICs"
|
|
HOST_VNICS = "host.VNICs"
|
|
VMS = "vms"
|
|
|
|
value_trans = {'translation-type': 'VALUE'}
|
|
|
|
vms_translator = {
|
|
'translation-type': 'HDICT',
|
|
'table-name': VMS,
|
|
'selector-type': 'DICT_SELECTOR',
|
|
'field-translators':
|
|
({'fieldname': 'name', 'translator': value_trans},
|
|
{'fieldname': 'uuid', 'translator': value_trans},
|
|
{'fieldname': 'host_uuid', 'translator': value_trans},
|
|
{'fieldname': 'pathName', 'translator': value_trans},
|
|
{'fieldname': 'status', 'translator': value_trans},
|
|
{'fieldname': 'CpuDemand', 'translator': value_trans},
|
|
{'fieldname': 'CpuUsage', 'translator': value_trans},
|
|
{'fieldname': 'memorySizeMB', 'translator': value_trans},
|
|
{'fieldname': 'MemoryUsage', 'translator': value_trans},
|
|
{'fieldname': 'committedStorage', 'translator': value_trans},
|
|
{'fieldname': 'uncommittedStorage', 'translator': value_trans},
|
|
{'fieldname': 'annotation', 'translator': value_trans})}
|
|
|
|
pnic_translator = {
|
|
'translation-type': 'HDICT',
|
|
'table-name': HOST_PNICS,
|
|
'selector-type': 'DICT_SELECTOR',
|
|
'field-translators':
|
|
({'fieldname': 'host_uuid', 'translator': value_trans},
|
|
{'fieldname': 'device', 'translator': value_trans},
|
|
{'fieldname': 'mac', 'translator': value_trans},
|
|
{'fieldname': 'ipAddress', 'translator': value_trans},
|
|
{'fieldname': 'subnetMask', 'translator': value_trans})}
|
|
|
|
vnic_translator = {
|
|
'translation-type': 'HDICT',
|
|
'table-name': HOST_VNICS,
|
|
'selector-type': 'DICT_SELECTOR',
|
|
'field-translators':
|
|
({'fieldname': 'host_uuid', 'translator': value_trans},
|
|
{'fieldname': 'device', 'translator': value_trans},
|
|
{'fieldname': 'mac', 'translator': value_trans},
|
|
{'fieldname': 'portgroup', 'translator': value_trans},
|
|
{'fieldname': 'ipAddress', 'translator': value_trans},
|
|
{'fieldname': 'subnetMask', 'translator': value_trans})}
|
|
|
|
hosts_translator = {
|
|
'translation-type': 'HDICT',
|
|
'table-name': HOSTS,
|
|
'selector-type': 'DICT_SELECTOR',
|
|
'field-translators':
|
|
({'fieldname': 'name', 'translator': value_trans},
|
|
{'fieldname': 'uuid', 'translator': value_trans},
|
|
{'fieldname': HOST_DNS, 'col': 'Host:DNS_id',
|
|
'translator': {'translation-type': 'LIST',
|
|
'table-name': HOST_DNS,
|
|
'id-col': 'Host:DNS_id',
|
|
'val-col': 'DNS_IPs',
|
|
'translator': value_trans}})}
|
|
|
|
TRANSLATORS = [hosts_translator, pnic_translator, vnic_translator,
|
|
vms_translator]
|
|
|
|
def __init__(self, name='', args=None, session=None):
|
|
if args is None:
|
|
args = self.empty_credentials()
|
|
else:
|
|
args['tenant_name'] = None
|
|
super(VCenterDriver, self).__init__(name, args=args)
|
|
datasource_driver.ExecutionDriver.__init__(self)
|
|
try:
|
|
self.max_VMs = int(args['max_vms'])
|
|
except (KeyError, ValueError):
|
|
LOG.warning("max_vms has not been configured, "
|
|
" defaulting to 999.")
|
|
self.max_VMs = 999
|
|
try:
|
|
self.max_Hosts = int(args['max_hosts'])
|
|
except (KeyError, ValueError):
|
|
LOG.warning("max_hosts has not been configured, "
|
|
"defaulting to 999.")
|
|
self.max_Hosts = 999
|
|
self.hosts = None
|
|
self.creds = args
|
|
self.session = session
|
|
if session is None:
|
|
self.session = api.VMwareAPISession(self.creds['auth_url'],
|
|
self.creds['username'],
|
|
self.creds['password'],
|
|
10, 1,
|
|
create_session=True)
|
|
self._init_end_start_poll()
|
|
|
|
@staticmethod
|
|
def get_datasource_info():
|
|
result = {}
|
|
result['id'] = 'vcenter'
|
|
result['description'] = ('Datasource driver that interfaces with '
|
|
'vcenter')
|
|
result['config'] = {'auth_url': constants.REQUIRED,
|
|
'username': constants.REQUIRED,
|
|
'password': constants.REQUIRED,
|
|
'poll_time': constants.OPTIONAL,
|
|
'max_vms': constants.OPTIONAL,
|
|
'max_hosts': constants.OPTIONAL}
|
|
result['secret'] = ['password']
|
|
|
|
return result
|
|
|
|
def update_from_datasource(self):
|
|
"""Called when it is time to pull new data from this datasource.
|
|
|
|
Pulls lists of objects from vCenter, if the data does not match
|
|
the correspondig table in the driver's raw state or has not yet been
|
|
added to the state, the driver calls methods to parse this data.
|
|
"""
|
|
|
|
hosts, pnics, vnics = self._get_hosts_and_nics()
|
|
self._translate_hosts(hosts)
|
|
self._translate_pnics(pnics)
|
|
self._translate_vnics(vnics)
|
|
|
|
vms = self._get_vms()
|
|
self._translate_vms(vms)
|
|
|
|
@ds_utils.update_state_on_changed(HOSTS)
|
|
def _translate_hosts(self, hosts):
|
|
"""Translate the host data from vCenter."""
|
|
|
|
row_data = VCenterDriver.convert_objs(hosts,
|
|
VCenterDriver.hosts_translator)
|
|
return row_data
|
|
|
|
@ds_utils.update_state_on_changed(HOST_PNICS)
|
|
def _translate_pnics(self, pnics):
|
|
"""Translate the host pnics data from vCenter."""
|
|
|
|
row_data = VCenterDriver.convert_objs(pnics,
|
|
VCenterDriver.pnic_translator)
|
|
return row_data
|
|
|
|
@ds_utils.update_state_on_changed(HOST_VNICS)
|
|
def _translate_vnics(self, vnics):
|
|
"""Translate the host vnics data from vCenter."""
|
|
|
|
row_data = VCenterDriver.convert_objs(vnics,
|
|
VCenterDriver.vnic_translator)
|
|
return row_data
|
|
|
|
def _get_hosts_and_nics(self):
|
|
"""Convert vCenter host object to simple format.
|
|
|
|
First the raw host data acquired from vCenter is parsed and
|
|
organized into a simple format that can be read by congress
|
|
translators. This creates three lists, hosts, pnics and vnics.
|
|
These lists are then parsed by congress translators to create tables.
|
|
"""
|
|
rawhosts = self._get_hosts_from_vcenter()
|
|
hosts = []
|
|
pnics = []
|
|
vnics = []
|
|
for host in rawhosts['objects']:
|
|
h = {}
|
|
h['vCenter_id'] = host.obj['value']
|
|
for prop in host['propSet']:
|
|
if prop.name == "hardware.systemInfo.uuid":
|
|
h['uuid'] = prop.val
|
|
break
|
|
for prop in host['propSet']:
|
|
if prop.name == "name":
|
|
h['name'] = prop.val
|
|
continue
|
|
if prop.name == "config.network.dnsConfig.address":
|
|
try:
|
|
h[self.HOST_DNS] = prop.val.string
|
|
except AttributeError:
|
|
h[self.HOST_DNS] = ["No DNS IP adddresses configured"]
|
|
continue
|
|
if prop.name == "config.network.pnic":
|
|
for pnic in prop.val.PhysicalNic:
|
|
p = {}
|
|
p['host_uuid'] = h['uuid']
|
|
p['mac'] = pnic['mac']
|
|
p['device'] = pnic['device']
|
|
p['ipAddress'] = pnic['spec']['ip']['ipAddress']
|
|
p['subnetMask'] = pnic['spec']['ip']['subnetMask']
|
|
pnics.append(p)
|
|
if prop.name == "config.network.vnic":
|
|
for vnic in prop.val.HostVirtualNic:
|
|
v = {}
|
|
v['host_uuid'] = h['uuid']
|
|
v['device'] = vnic['device']
|
|
v['portgroup'] = vnic['portgroup']
|
|
v['mac'] = vnic['spec']['mac']
|
|
v['ipAddress'] = vnic['spec']['ip']['ipAddress']
|
|
v['subnetMask'] = vnic['spec']['ip']['subnetMask']
|
|
vnics.append(v)
|
|
hosts.append(h)
|
|
# cached the hosts for vms
|
|
self.hosts = hosts
|
|
return hosts, pnics, vnics
|
|
|
|
@ds_utils.update_state_on_changed(VMS)
|
|
def _translate_vms(self, vms):
|
|
"""Translate the VM data from vCenter."""
|
|
|
|
row_data = VCenterDriver.convert_objs(vms,
|
|
VCenterDriver.vms_translator)
|
|
return row_data
|
|
|
|
def _get_vms(self):
|
|
rawvms = self._get_vms_from_vcenter()
|
|
vms = []
|
|
for vm in rawvms['objects']:
|
|
v = {}
|
|
for prop in vm['propSet']:
|
|
if prop.name == "name":
|
|
v['name'] = prop.val
|
|
continue
|
|
if prop.name == "config.uuid":
|
|
v['uuid'] = prop.val
|
|
continue
|
|
if prop.name == "config.annotation":
|
|
v['annotation'] = prop.val
|
|
continue
|
|
if prop.name == "summary.config.vmPathName":
|
|
v['pathName'] = prop.val
|
|
continue
|
|
if prop.name == "summary.config.memorySizeMB":
|
|
v['memorySizeMB'] = prop.val
|
|
continue
|
|
if prop.name == "summary.quickStats":
|
|
v['MemoryUsage'] = prop.val['guestMemoryUsage']
|
|
v['CpuDemand'] = prop.val['overallCpuDemand']
|
|
v['CpuUsage'] = prop.val['overallCpuUsage']
|
|
continue
|
|
if prop.name == "summary.overallStatus":
|
|
v['status'] = prop.val
|
|
if prop.name == "summary.storage":
|
|
v['committedStorage'] = prop.val['committed']
|
|
v['uncommittedStorage'] = prop.val['uncommitted']
|
|
continue
|
|
if prop.name == 'runtime.host':
|
|
for host in self.hosts:
|
|
if host['vCenter_id'] == prop.val['value']:
|
|
v['host_uuid'] = host['uuid']
|
|
continue
|
|
continue
|
|
vms.append(v)
|
|
return vms
|
|
|
|
def _get_hosts_from_vcenter(self):
|
|
"""Called to pull host data from vCenter
|
|
|
|
"""
|
|
|
|
dataFields = ['name',
|
|
'hardware.systemInfo.uuid',
|
|
'config.network.dnsConfig.address',
|
|
'config.network.pnic',
|
|
'config.network.vnic']
|
|
return self.session.invoke_api(vim_util, 'get_objects',
|
|
self.session.vim, 'HostSystem',
|
|
self.max_Hosts, dataFields)
|
|
|
|
def _get_vms_from_vcenter(self):
|
|
"""Called to pull VM data from vCenter
|
|
|
|
"""
|
|
|
|
dataFields = ['name',
|
|
'config.uuid',
|
|
'config.annotation',
|
|
'summary.config.vmPathName',
|
|
'runtime.host',
|
|
'summary.config.memorySizeMB',
|
|
'summary.quickStats',
|
|
'summary.overallStatus',
|
|
'summary.storage']
|
|
return self.session.invoke_api(vim_util, 'get_objects',
|
|
self.session.vim, 'VirtualMachine',
|
|
self.max_VMs, dataFields)
|
|
|
|
def execute(self, action, action_args):
|
|
"""Overwrite ExecutionDriver.execute()."""
|
|
# action can be written as a method or an API call.
|
|
func = getattr(self, action, None)
|
|
if func and self.is_executable(func):
|
|
func(action_args)
|
|
else:
|
|
self._execute_api(self.session, action, action_args)
|