# 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)