vmware-nsx/quantum/plugins/hyperv/agent/utils.py
Alessandro Pilotti 424b0d6ab6 Fixes Hyper-V agent issue with mixed network types
Fixes bug: 1153274

Due to a bug in setting up external ports the Hyper-V agent fails to work in
scenarios with VLAN and flat networks.

Furthermore, a warning message on Hyper-V 2012 indicates that external
port settings are ignored. As a result the agent can be simplified by
avoiding vswitch external ports configurations.

Change-Id: I860d22f427cb10dbeac422c1db2b3bcf9e84150f
2013-03-11 02:35:11 +02:00

227 lines
8.7 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Cloudbase Solutions SRL
# Copyright 2013 Pedro Navarro Perez
# 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: Pedro Navarro Perez
# @author: Alessandro Pilotti, Cloudbase Solutions Srl
import sys
import time
from oslo.config import cfg
from quantum.common import exceptions as q_exc
from quantum.openstack.common import log as logging
# Check needed for unit testing on Unix
if sys.platform == 'win32':
import wmi
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class HyperVException(q_exc.QuantumException):
message = _('HyperVException: %(msg)s')
WMI_JOB_STATE_RUNNING = 4
WMI_JOB_STATE_COMPLETED = 7
class HyperVUtils(object):
def __init__(self):
self._wmi_conn = None
@property
def _conn(self):
if self._wmi_conn is None:
self._wmi_conn = wmi.WMI(moniker='//./root/virtualization')
return self._wmi_conn
def get_switch_ports(self, vswitch_name):
vswitch = self._get_vswitch(vswitch_name)
vswitch_ports = vswitch.associators(
wmi_result_class='Msvm_SwitchPort')
return set(p.Name for p in vswitch_ports)
def vnic_port_exists(self, port_id):
try:
self._get_vnic_settings(port_id)
except Exception:
return False
return True
def get_vnic_ids(self):
return set(
p.ElementName
for p in self._conn.Msvm_SyntheticEthernetPortSettingData())
def _get_vnic_settings(self, vnic_name):
vnic_settings = self._conn.Msvm_SyntheticEthernetPortSettingData(
ElementName=vnic_name)
if not len(vnic_settings):
raise HyperVException(msg=_('Vnic not found: %s') % vnic_name)
return vnic_settings[0]
def connect_vnic_to_vswitch(self, vswitch_name, switch_port_name):
vnic_settings = self._get_vnic_settings(switch_port_name)
if not vnic_settings.Connection or not vnic_settings.Connection[0]:
port = self.get_port_by_id(switch_port_name, vswitch_name)
if port:
port_path = port.Path_()
else:
port_path = self._create_switch_port(
vswitch_name, switch_port_name)
vnic_settings.Connection = [port_path]
self._modify_virt_resource(vnic_settings)
def _get_vm_from_res_setting_data(self, res_setting_data):
sd = res_setting_data.associators(
wmi_result_class='Msvm_VirtualSystemSettingData')
vm = sd[0].associators(
wmi_result_class='Msvm_ComputerSystem')
return vm[0]
def _modify_virt_resource(self, res_setting_data):
vm = self._get_vm_from_res_setting_data(res_setting_data)
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
(job_path,
ret_val) = vs_man_svc.ModifyVirtualSystemResources(
vm.Path_(), [res_setting_data.GetText_(1)])
self._check_job_status(ret_val, job_path)
def _check_job_status(self, ret_val, jobpath):
"""Poll WMI job state for completion"""
if not ret_val:
return
elif ret_val != WMI_JOB_STATE_RUNNING:
raise HyperVException(msg=_('Job failed with error %d' % ret_val))
job_wmi_path = jobpath.replace('\\', '/')
job = wmi.WMI(moniker=job_wmi_path)
while job.JobState == WMI_JOB_STATE_RUNNING:
time.sleep(0.1)
job = wmi.WMI(moniker=job_wmi_path)
if job.JobState != WMI_JOB_STATE_COMPLETED:
job_state = job.JobState
if job.path().Class == "Msvm_ConcreteJob":
err_sum_desc = job.ErrorSummaryDescription
err_desc = job.ErrorDescription
err_code = job.ErrorCode
raise HyperVException(
msg=_("WMI job failed with status %(job_state)d. "
"Error details: %(err_sum_desc)s - %(err_desc)s - "
"Error code: %(err_code)d") % locals())
else:
(error, ret_val) = job.GetError()
if not ret_val and error:
raise HyperVException(
msg=_("WMI job failed with status %(job_state)d. "
"Error details: %(error)s") % locals())
else:
raise HyperVException(
msg=_("WMI job failed with status %(job_state)d. "
"No error description available") % locals())
desc = job.Description
elap = job.ElapsedTime
LOG.debug(_("WMI job succeeded: %(desc)s, Elapsed=%(elap)s") %
locals())
def _create_switch_port(self, vswitch_name, switch_port_name):
""" Creates a switch port """
switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0]
vswitch_path = self._get_vswitch(vswitch_name).path_()
(new_port, ret_val) = switch_svc.CreateSwitchPort(
Name=switch_port_name,
FriendlyName=switch_port_name,
ScopeOfResidence="",
VirtualSwitch=vswitch_path)
if ret_val != 0:
raise HyperVException(
msg=_('Failed creating port for %s') % vswitch_name)
return new_port
def disconnect_switch_port(
self, vswitch_name, switch_port_name, delete_port):
""" Disconnects the switch port """
switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0]
switch_port_path = self._get_switch_port_path_by_name(
switch_port_name)
if not switch_port_path:
# Port not found. It happens when the VM was already deleted.
return
(ret_val, ) = switch_svc.DisconnectSwitchPort(
SwitchPort=switch_port_path)
if ret_val != 0:
raise HyperVException(
msg=_('Failed to disconnect port %(switch_port_name)s '
'from switch %(vswitch_name)s '
'with error %(ret_val)s') % locals())
if delete_port:
(ret_val, ) = switch_svc.DeleteSwitchPort(
SwitchPort=switch_port_path)
if ret_val != 0:
raise HyperVException(
msg=_('Failed to delete port %(switch_port_name)s '
'from switch %(vswitch_name)s '
'with error %(ret_val)s') % locals())
def _get_vswitch(self, vswitch_name):
vswitch = self._conn.Msvm_VirtualSwitch(ElementName=vswitch_name)
if not len(vswitch):
raise 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='Msvm_SwitchPort')
for vswitch_port in vswitch_ports:
lan_endpoints = vswitch_port.associators(
wmi_result_class='Msvm_SwitchLanEndpoint')
if len(lan_endpoints):
ext_port = lan_endpoints[0].associators(
wmi_result_class='Msvm_ExternalEthernetPort')
if ext_port:
return vswitch_port
def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name):
vlan_endpoint_settings = self._conn.Msvm_VLANEndpointSettingData(
ElementName=switch_port_name)[0]
if vlan_endpoint_settings.AccessVLAN != vlan_id:
vlan_endpoint_settings.AccessVLAN = vlan_id
vlan_endpoint_settings.put()
def _get_switch_port_path_by_name(self, switch_port_name):
vswitch = self._conn.Msvm_SwitchPort(ElementName=switch_port_name)
if vswitch:
return vswitch[0].path_()
def get_vswitch_id(self, vswitch_name):
vswitch = self._get_vswitch(vswitch_name)
return vswitch.Name
def get_port_by_id(self, port_id, vswitch_name):
vswitch = self._get_vswitch(vswitch_name)
switch_ports = vswitch.associators(wmi_result_class='Msvm_SwitchPort')
for switch_port in switch_ports:
if (switch_port.ElementName == port_id):
return switch_port