7af673ef1a
Change-Id: I2beecfd13617fb243008016b810f49db5593fc65 Closes-Bug: 1252712
199 lines
8.4 KiB
Python
199 lines
8.4 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2013 Cloudbase Solutions SRL
|
|
# 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.
|
|
# @author: Alessandro Pilotti, Cloudbase Solutions Srl
|
|
# @author: Claudiu Belu, Cloudbase Solutions Srl
|
|
|
|
from neutron.plugins.hyperv.agent import utils
|
|
|
|
|
|
class HyperVUtilsV2(utils.HyperVUtils):
|
|
|
|
_EXTERNAL_PORT = 'Msvm_ExternalEthernetPort'
|
|
_ETHERNET_SWITCH_PORT = 'Msvm_EthernetSwitchPort'
|
|
_PORT_ALLOC_SET_DATA = 'Msvm_EthernetPortAllocationSettingData'
|
|
_PORT_VLAN_SET_DATA = 'Msvm_EthernetSwitchPortVlanSettingData'
|
|
_PORT_ALLOC_ACL_SET_DATA = 'Msvm_EthernetSwitchPortAclSettingData'
|
|
_LAN_ENDPOINT = 'Msvm_LANEndpoint'
|
|
_STATE_DISABLED = 3
|
|
_OPERATION_MODE_ACCESS = 1
|
|
|
|
_ACL_DIR_IN = 1
|
|
_ACL_DIR_OUT = 2
|
|
_ACL_TYPE_IPV4 = 2
|
|
_ACL_TYPE_IPV6 = 3
|
|
_ACL_ACTION_METER = 3
|
|
_ACL_APPLICABILITY_LOCAL = 1
|
|
|
|
_wmi_namespace = '//./root/virtualization/v2'
|
|
|
|
def __init__(self):
|
|
super(HyperVUtilsV2, self).__init__()
|
|
|
|
def connect_vnic_to_vswitch(self, vswitch_name, switch_port_name):
|
|
vnic = self._get_vnic_settings(switch_port_name)
|
|
vswitch = self._get_vswitch(vswitch_name)
|
|
|
|
port, found = self._get_switch_port_allocation(switch_port_name, True)
|
|
port.HostResource = [vswitch.path_()]
|
|
port.Parent = vnic.path_()
|
|
if not found:
|
|
vm = self._get_vm_from_res_setting_data(vnic)
|
|
self._add_virt_resource(vm, port)
|
|
else:
|
|
self._modify_virt_resource(port)
|
|
|
|
def _modify_virt_resource(self, res_setting_data):
|
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
(job_path, out_set_data, ret_val) = vs_man_svc.ModifyResourceSettings(
|
|
ResourceSettings=[res_setting_data.GetText_(1)])
|
|
self._check_job_status(ret_val, job_path)
|
|
|
|
def _add_virt_resource(self, vm, res_setting_data):
|
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
(job_path, out_set_data, ret_val) = vs_man_svc.AddResourceSettings(
|
|
vm.path_(), [res_setting_data.GetText_(1)])
|
|
self._check_job_status(ret_val, job_path)
|
|
|
|
def _remove_virt_resource(self, res_setting_data):
|
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
(job, ret_val) = vs_man_svc.RemoveResourceSettings(
|
|
ResourceSettings=[res_setting_data.path_()])
|
|
self._check_job_status(ret_val, job)
|
|
|
|
def _add_virt_feature(self, element, res_setting_data):
|
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
(job_path, out_set_data, ret_val) = vs_man_svc.AddFeatureSettings(
|
|
element.path_(), [res_setting_data.GetText_(1)])
|
|
self._check_job_status(ret_val, job_path)
|
|
|
|
def disconnect_switch_port(
|
|
self, vswitch_name, switch_port_name, delete_port):
|
|
"""Disconnects the switch port."""
|
|
sw_port, found = self._get_switch_port_allocation(switch_port_name)
|
|
if not sw_port:
|
|
# Port not found. It happens when the VM was already deleted.
|
|
return
|
|
|
|
if delete_port:
|
|
self._remove_virt_resource(sw_port)
|
|
else:
|
|
sw_port.EnabledState = self._STATE_DISABLED
|
|
self._modify_virt_resource(sw_port)
|
|
|
|
def _get_vswitch(self, vswitch_name):
|
|
vswitch = self._conn.Msvm_VirtualEthernetSwitch(
|
|
ElementName=vswitch_name)
|
|
if not len(vswitch):
|
|
raise utils.HyperVException(msg=_('VSwitch not found: %s') %
|
|
vswitch_name)
|
|
return vswitch[0]
|
|
|
|
def _get_vswitch_external_port(self, vswitch):
|
|
vswitch_ports = vswitch.associators(
|
|
wmi_result_class=self._ETHERNET_SWITCH_PORT)
|
|
for vswitch_port in vswitch_ports:
|
|
lan_endpoints = vswitch_port.associators(
|
|
wmi_result_class=self._LAN_ENDPOINT)
|
|
if len(lan_endpoints):
|
|
lan_endpoints = lan_endpoints[0].associators(
|
|
wmi_result_class=self._LAN_ENDPOINT)
|
|
if len(lan_endpoints):
|
|
ext_port = lan_endpoints[0].associators(
|
|
wmi_result_class=self._EXTERNAL_PORT)
|
|
if ext_port:
|
|
return vswitch_port
|
|
|
|
def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name):
|
|
port_alloc, found = self._get_switch_port_allocation(switch_port_name)
|
|
if not found:
|
|
raise utils.HyperVException(
|
|
msg=_('Port Alloc not found: %s') % switch_port_name)
|
|
|
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
vlan_settings = self._get_vlan_setting_data_from_port_alloc(port_alloc)
|
|
if vlan_settings:
|
|
# Removing the feature because it cannot be modified
|
|
# due to a wmi exception.
|
|
(job_path, ret_val) = vs_man_svc.RemoveFeatureSettings(
|
|
FeatureSettings=[vlan_settings.path_()])
|
|
self._check_job_status(ret_val, job_path)
|
|
|
|
(vlan_settings, found) = self._get_vlan_setting_data(switch_port_name)
|
|
vlan_settings.AccessVlanId = vlan_id
|
|
vlan_settings.OperationMode = self._OPERATION_MODE_ACCESS
|
|
(job_path, out, ret_val) = vs_man_svc.AddFeatureSettings(
|
|
port_alloc.path_(), [vlan_settings.GetText_(1)])
|
|
self._check_job_status(ret_val, job_path)
|
|
|
|
def _get_vlan_setting_data_from_port_alloc(self, port_alloc):
|
|
return self._get_first_item(port_alloc.associators(
|
|
wmi_result_class=self._PORT_VLAN_SET_DATA))
|
|
|
|
def _get_vlan_setting_data(self, switch_port_name, create=True):
|
|
return self._get_setting_data(
|
|
self._PORT_VLAN_SET_DATA,
|
|
switch_port_name, create)
|
|
|
|
def _get_switch_port_allocation(self, switch_port_name, create=False):
|
|
return self._get_setting_data(
|
|
self._PORT_ALLOC_SET_DATA,
|
|
switch_port_name, create)
|
|
|
|
def _get_setting_data(self, class_name, element_name, create=True):
|
|
element_name = element_name.replace("'", '"')
|
|
q = self._conn.query("SELECT * FROM %(class_name)s WHERE "
|
|
"ElementName = '%(element_name)s'" %
|
|
{"class_name": class_name,
|
|
"element_name": element_name})
|
|
data = self._get_first_item(q)
|
|
found = data is not None
|
|
if not data and create:
|
|
data = self._get_default_setting_data(class_name)
|
|
data.ElementName = element_name
|
|
return data, found
|
|
|
|
def _get_default_setting_data(self, class_name):
|
|
return self._conn.query("SELECT * FROM %s WHERE InstanceID "
|
|
"LIKE '%%\\Default'" % class_name)[0]
|
|
|
|
def _get_first_item(self, obj):
|
|
if obj:
|
|
return obj[0]
|
|
|
|
def enable_port_metrics_collection(self, switch_port_name):
|
|
port, found = self._get_switch_port_allocation(switch_port_name, False)
|
|
if not found:
|
|
return
|
|
|
|
# Add the ACLs only if they don't already exist
|
|
acls = port.associators(wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA)
|
|
for acl_type in [self._ACL_TYPE_IPV4, self._ACL_TYPE_IPV6]:
|
|
for acl_dir in [self._ACL_DIR_IN, self._ACL_DIR_OUT]:
|
|
acls = [v for v in acls
|
|
if v.Action == self._ACL_ACTION_METER and
|
|
v.Applicability == self._ACL_APPLICABILITY_LOCAL and
|
|
v.Direction == acl_dir and
|
|
v.AclType == acl_type]
|
|
if not acls:
|
|
acl = self._get_default_setting_data(
|
|
self._PORT_ALLOC_ACL_SET_DATA)
|
|
acl.AclType = acl_type
|
|
acl.Direction = acl_dir
|
|
acl.Action = self._ACL_ACTION_METER
|
|
acl.Applicability = self._ACL_APPLICABILITY_LOCAL
|
|
self._add_virt_feature(port, acl)
|