new file mode 100644
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <configSections>
+ <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+ <section name="OvsDiscoveryAgent.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
+ </sectionGroup>
+ </configSections>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+ <userSettings>
+ <OvsDiscoveryAgent.Properties.Settings>
+ <setting name="OvsBridge" serializeAs="String">
+ <value>ovs-int</value>
+ </setting>
+ </OvsDiscoveryAgent.Properties.Settings>
+ </userSettings>
+</configuration>
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{2563C1BE-B240-4F63-84C5-01D98D015A3F}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>OvsDiscoveryAgent</RootNamespace>
+ <AssemblyName>OvsDiscoveryAgent</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Management" />
+ <Reference Include="System.ServiceProcess" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="OvsVsctl.cs" />
+ <Compile Include="Properties\Settings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTimeSharedInput>True</DesignTimeSharedInput>
+ <DependentUpon>Settings.settings</DependentUpon>
+ </Compile>
+ <Compile Include="VirtualAdapterManager.cs" />
+ <Compile Include="OvsDiscoveryService.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="OvsSwitchMonitor.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="VirtualAdapter.cs" />
+ <Compile Include="VirtualAdapterMonitor.cs" />
+ <Compile Include="WmiMonitor.cs" />
+ <Compile Include="WqlCondition.cs" />
+ <Compile Include="WqlHelper.cs" />
+ <Compile Include="WqlObject.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OvsDiscoveryAgent", "OvsDiscoveryAgent.csproj", "{2563C1BE-B240-4F63-84C5-01D98D015A3F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|x64.Build.0 = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Debug|x86.Build.0 = Debug|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|x64.ActiveCfg = Release|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|x64.Build.0 = Release|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|x86.ActiveCfg = Release|Any CPU
+ {2563C1BE-B240-4F63-84C5-01D98D015A3F}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
new file mode 100644
@@ -0,0 +1,67 @@
+using System.ServiceProcess;
+using System.Threading;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace OvsDiscoveryAgent
+{
+ public class OvsDiscoveryService: ServiceBase
+ {
+#region Console related functions
+ [DllImport("Kernel32")]
+ private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
+
+ private delegate bool EventHandler(CtrlType sig);
+ static EventHandler _handler;
+
+ enum CtrlType
+ {
+ CTRL_C_EVENT = 0,
+ CTRL_BREAK_EVENT = 1,
+ CTRL_CLOSE_EVENT = 2,
+ CTRL_LOGOFF_EVENT = 5,
+ CTRL_SHUTDOWN_EVENT = 6
+ }
+
+ private bool Handler(CtrlType sig)
+ {
+ switch (sig)
+ {
+ case CtrlType.CTRL_C_EVENT:
+ case CtrlType.CTRL_LOGOFF_EVENT:
+ case CtrlType.CTRL_SHUTDOWN_EVENT:
+ case CtrlType.CTRL_CLOSE_EVENT:
+ default:
+ OnStop();
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Start the service as a console application.
+ /// </summary>
+ /// <param name="args">Startup parameters.</param>
+ public void Start(string[] args)
+ {
+ _handler += Handler;
+ OnStart(args);
+ (new ManualResetEvent(false)).WaitOne();
+ }
+#endregion
+
+ protected override void OnStart(string[] args)
+ {
+ var worker = new BackgroundWorker();
+ worker.DoWork += (s, e) =>
+ {
+ VirtualAdapterManager.Instance.Start();
+ };
+ worker.RunWorkerAsync();
+ }
+
+ protected override void OnStop()
+ {
+ VirtualAdapterManager.Instance.Stop();
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,73 @@
+using System.Management;
+
+namespace OvsDiscoveryAgent
+{
+ public class OvsSwitch
+ {
+ public string Id { get; set; }
+ public override string ToString()
+ {
+ return "{" + Id + "}";
+ }
+ }
+ public class OvsSwitchMonitor : WmiMonitor<OvsSwitch>
+ {
+ #region Consts
+ private static readonly string ovsExtentionName = "Open vSwitch Extension";
+ private static readonly WqlCondition isOvsExtensionCondition = new WqlBasicCondition(WqlColumns.ElementName, ovsExtentionName);
+ private static readonly WqlCondition isEnabledCondition = new WqlBasicCondition<int>(WqlColumns.EnabledState, (int)EnabledState.Enabled);
+ #endregion
+ protected OvsSwitchMonitor() { }
+ private static OvsSwitchMonitor monitorInstance;
+ public static OvsSwitchMonitor Instance
+ {
+ get
+ {
+ if (monitorInstance == null)
+ {
+ monitorInstance = new OvsSwitchMonitor();
+ }
+ return monitorInstance;
+ }
+ }
+
+ protected override ManagementObjectCollection QueryItems()
+ {
+ return WqlHelper.QueryAll(WqlTables.EthernetSwitchExtension,
+ isOvsExtensionCondition & isEnabledCondition,
+ new[] { WqlColumns.SystemName, WqlColumns.EnabledState });
+ }
+ protected override string GetItemId(OvsSwitch item)
+ {
+ return item.Id;
+ }
+
+ protected override OvsSwitch ConvertItem(ManagementBaseObject mbo)
+ {
+ if (mbo == null) return null;
+ if ((EnabledState)((ushort)mbo[WqlColumns.EnabledState]) != EnabledState.Enabled)
+ {
+ return null;
+ }
+ var result = new OvsSwitch();
+ result.Id = mbo[WqlColumns.SystemName] as string;
+ return result;
+ }
+
+ protected override bool IsSameItem(OvsSwitch oldItem, OvsSwitch newItem)
+ {
+ bool oldEnabled = (oldItem != null);
+ bool newEnabled = (newItem != null);
+ if (oldEnabled != newEnabled) return false;
+ if (oldEnabled && oldItem.Id != newItem.Id) return false;
+ return true;
+ }
+
+ protected override ManagementEventWatcher CreateEventWatcher()
+ {
+ return WqlHelper.GetEventWatcher(WqlEventType.Operation,
+ WqlTables.EthernetSwitchExtension,
+ isOvsExtensionCondition);
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OvsDiscoveryAgent
+{
+ /// <summary>
+ /// Provide basic ovs-vsctl functionality.
+ /// </summary>
+ /// <remarks>
+ /// TODO: This class assumes that ovs-vsctl is in the default PATH and calls
+ /// it as an external process. In its final format, this class should be a
+ /// wrapper around ovs-vsctl.c and built as a C++ CLR DLL project.
+ /// </remarks>
+ public static class OvsVsctl
+ {
+ private struct CommandResult
+ {
+ public int ExitCode;
+ public string OutputMessage;
+ public string ErrorMessage;
+ }
+ private static Process RunCommandAsync(string command, string args)
+ {
+ Trace.TraceInformation("[Console] {0} {1}", command, args);
+ Process process = new Process();
+ ProcessStartInfo startInfo = new ProcessStartInfo
+ {
+ WindowStyle = ProcessWindowStyle.Hidden,
+ FileName = command,
+ Arguments = args,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false
+ };
+ process.StartInfo = startInfo;
+ process.Start();
+ return process;
+ }
+ private static CommandResult RunCommand(string command, string args)
+ {
+ int maxTimeoutMs = 1000;
+ var p = RunCommandAsync(command, args);
+ var result = new CommandResult();
+ if (!p.WaitForExit(maxTimeoutMs))
+ {
+ p.Kill();
+ string errorMsg = string.Format("Command '{0} {1}' did not return within {2} milliseconds.",
+ command, args, maxTimeoutMs);
+ Trace.TraceError(errorMsg);
+ result.ExitCode = -1;
+ result.ErrorMessage = errorMsg;
+ }
+ else
+ {
+ result.ExitCode = p.ExitCode;
+ result.OutputMessage = p.StandardOutput.ReadToEnd();
+ result.ErrorMessage = p.StandardError.ReadToEnd();
+ Trace.TraceInformation("Command '{0} {1}' returns {2}, stdout='{3}', stderr='{4}'",
+ command, args, p.ExitCode, result.OutputMessage, result.ErrorMessage);
+ }
+ return result;
+ }
+ public static void AddPort(string bridge, string port, string[] settings, bool mayExist = true)
+ {
+ // Example: ovs-vsctl add-port <bridge> <port> -- set interface <port> external_ids:iface-id=<uuid>
+ // TODO: There is a bug in ovs kernel that result in "ovs-vsctl add-port" returning an error
+ // when VM has not been powered on. We are ignoring error for now.
+ RunCommand("ovs-vsctl.exe", string.Join(" ", mayExist?"--may-exist":string.Empty,
+ "add-port", bridge, port, string.Join(" ", settings)));
+ }
+ public static void DeletePort(string bridge, string port)
+ {
+ // Example: ovs-vsctl del-port <bridge> <port>
+ // TODO: There is a bug in ovs userspace that result in "ovs-vsctl del-port" always hanging forever
+ // when OVS extension has been disabled. This results in we always have to kill the process when
+ // we delete the ports connected to the disabled switch.
+ RunCommand("ovs-vsctl.exe", string.Format("del-port {0} {1}", bridge, port));
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,26 @@
+using System;
+using System.ServiceProcess;
+
+namespace OvsDiscoveryAgent
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if (!Environment.UserInteractive)
+ {
+ ServiceBase[] ServicesToRun;
+ ServicesToRun = new ServiceBase[]
+ {
+ new OvsDiscoveryService()
+ };
+ ServiceBase.Run(ServicesToRun);
+ }
+ else
+ {
+ OvsDiscoveryService service = new OvsDiscoveryService();
+ service.Start(args);
+ }
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OvsDiscoveryAgent")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("OvsDiscoveryAgent")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2563c1be-b240-4f63-84c5-01d98d015a3f")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
new file mode 100644
@@ -0,0 +1,38 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace OvsDiscoveryAgent.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("ovs-int")]
+ public string OvsBridge {
+ get {
+ return ((string)(this["OvsBridge"]));
+ }
+ set {
+ this["OvsBridge"] = value;
+ }
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="OvsDiscoveryAgent.Properties" GeneratedClassName="Settings">
+ <Profiles />
+ <Settings>
+ <Setting Name="OvsBridge" Type="System.String" Scope="User">
+ <Value Profile="(Default)">ovs-int</Value>
+ </Setting>
+ </Settings>
+</SettingsFile>
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,36 @@
+using System.Management;
+
+namespace OvsDiscoveryAgent
+{
+ public class VirtualAdapter
+ {
+ public VirtualAdapter()
+ {
+ SwitchId = OvsPortName = Id = VmId = string.Empty;
+ }
+ public VirtualAdapter(string id, string vmId, string switchId, string ovsPort)
+ {
+ Id = id;
+ SwitchId = switchId;
+ OvsPortName = ovsPort;
+ }
+ public string SwitchId { get; set; }
+ public string OvsPortName { get; set; }
+ public string Id { get; set; }
+ public string VmId { get; set; }
+ public ManagementBaseObject SourceWmiObject { get; set; }
+ public override bool Equals(object obj)
+ {
+ var otherAdapter = obj as VirtualAdapter;
+ if (otherAdapter == null) return false;
+ return SwitchId == otherAdapter.SwitchId &&
+ OvsPortName == otherAdapter.OvsPortName &&
+ Id == otherAdapter.Id &&
+ VmId == otherAdapter.VmId;
+ }
+ public override int GetHashCode()
+ {
+ return Id.GetHashCode();
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,228 @@
+using System;
+using System.Diagnostics;
+using System.Management;
+
+namespace OvsDiscoveryAgent
+{
+ public class VirtualAdapterManager
+ {
+ protected VirtualAdapterManager() { }
+ private static VirtualAdapterManager managerInstance;
+ public static VirtualAdapterManager Instance
+ {
+ get
+ {
+ if (managerInstance == null)
+ {
+ managerInstance = new VirtualAdapterManager();
+ }
+ return managerInstance;
+ }
+ }
+ public void Start()
+ {
+ OvsSwitchMonitor.Instance.Subscribe();
+ VirtualAdapterMonitor.Instance.Subscribe();
+ FullSync();
+ OvsSwitchMonitor.Instance.ItemUpdated += OvsSwitchMonitor_ItemUpdated;
+ VirtualAdapterMonitor.Instance.ItemUpdated += VirtualAdapterMonitor_ItemUpdated;
+ }
+
+ protected void FullSync()
+ {
+ OvsSwitchMonitor.Instance.LoadAllItems();
+ VirtualAdapterMonitor.Instance.LoadAllItems();
+ OvsSwitchMonitor.Instance.EnterReadLock();
+ VirtualAdapterMonitor.Instance.EnterReadLock();
+ try
+ {
+ // New port is not connected to an OVS switch, ignoring it.
+ foreach (var port in VirtualAdapterMonitor.Instance.ItemsMap.Values)
+ {
+ if (OvsSwitchMonitor.Instance.Contains(port.SwitchId))
+ {
+ if (string.IsNullOrEmpty(port.OvsPortName))
+ {
+ LinkOvsPort(port);
+ // Once we link OVS port a Modification event will be triggered.
+ // Ovs port will be added at that time.
+ }
+ CreateOvsPort(port);
+ }
+ }
+ }
+ finally
+ {
+ OvsSwitchMonitor.Instance.ExitReadLock();
+ VirtualAdapterMonitor.Instance.ExitReadLock();
+ }
+ }
+
+ protected void CreateOvsPort(VirtualAdapter adapter)
+ {
+ if (string.IsNullOrEmpty(adapter.OvsPortName)) return;
+ var settings = new string[] { "--", "set", "interface", adapter.OvsPortName,
+ "external_ids:vm-id=" + adapter.VmId };
+ Trace.TraceInformation("ovs-vsctl: Adding OVS port {0} to bridge {1} with arguments '{2}'",
+ adapter.OvsPortName, Properties.Settings.Default.OvsBridge,
+ string.Join(" ", settings));
+ OvsVsctl.AddPort(Properties.Settings.Default.OvsBridge, adapter.OvsPortName, settings);
+ }
+
+ protected void DeleteOvsPort(VirtualAdapter adapter)
+ {
+ // Old port is not connected to an OVS switch, ignoring it.
+ if (string.IsNullOrEmpty(adapter.OvsPortName)) return;
+ Trace.TraceInformation("ovs-vsctl: Deleting OVS port {0} from bridge {1}",
+ adapter.OvsPortName, Properties.Settings.Default.OvsBridge);
+ OvsVsctl.DeletePort(Properties.Settings.Default.OvsBridge, adapter.OvsPortName);
+ }
+
+ protected void AssignOvsPortName(VirtualAdapter adapter)
+ {
+ // TODO: Find a more user friendly ovs port name than its UUID.
+ adapter.OvsPortName = adapter.Id;
+ }
+
+ protected void LinkOvsPort(VirtualAdapter adapter)
+ {
+ ManagementScope scope;
+ var vsms = WqlHelper.GetServiceObject(WqlTables.VirtualSystemManagementService, out scope);
+ var inParams = vsms.GetMethodParameters(WqlMethods.ModifyResourceSettings);
+ var newObj = (ManagementBaseObject)adapter.SourceWmiObject.Clone();
+ Trace.Assert(string.IsNullOrEmpty(adapter.OvsPortName));
+ AssignOvsPortName(adapter);
+ newObj[WqlColumns.ElementName] = adapter.OvsPortName;
+ string[] resources = new string[1];
+ resources[0] = newObj.GetText(TextFormat.CimDtd20);
+ inParams["ResourceSettings"] = resources;
+ var outParams = vsms.InvokeMethod(WqlMethods.ModifyResourceSettings, inParams, null);
+ if (outParams != null && ((UInt32)outParams["ReturnValue"] == ReturnCode.Started && WqlHelper.JobCompleted(outParams, scope) ||
+ (UInt32)outParams["ReturnValue"] == ReturnCode.Completed))
+ {
+ Trace.TraceInformation("Set OVS port {0} -> {1} completed successfully.",
+ adapter.OvsPortName, adapter.Id);
+ }
+ else
+ {
+ Trace.TraceError("Failed to set OVS port {0} -> {1}.", adapter.OvsPortName, adapter.Id);
+ }
+ }
+
+ protected void LinkOrCreateOvsPort(VirtualAdapter newPort)
+ {
+ if (string.IsNullOrEmpty(newPort.OvsPortName))
+ {
+ LinkOvsPort(newPort);
+ // Once we link OVS port a Modification event will be triggered.
+ // Ovs port will be added at that time.
+ }
+ else
+ {
+ // This handles the case where user add a Hyper-V VIF with OVS port
+ // specified.
+ CreateOvsPort(newPort);
+ }
+ }
+
+ protected bool IsOvsConnected(VirtualAdapter adapter)
+ {
+ return OvsSwitchMonitor.Instance.Contains(adapter.SwitchId);
+ }
+
+ private void VirtualAdapterMonitor_ItemUpdated(object sender, WmiMonitorEventArgs<VirtualAdapter> e)
+ {
+ OvsSwitchMonitor.Instance.EnterReadLock();
+ VirtualAdapterMonitor.Instance.EnterReadLock();
+ try
+ {
+ var newPort = e.NewValue;
+ var oldPort = e.OldValue;
+ switch (e.Operation)
+ {
+ case ItemOperation.Add:
+ // New port is not connected to an OVS switch, ignoring it.
+ if (IsOvsConnected(newPort))
+ {
+ LinkOrCreateOvsPort(newPort);
+ }
+ break;
+ case ItemOperation.Remove:
+ if (IsOvsConnected(oldPort))
+ {
+ DeleteOvsPort(oldPort);
+ }
+ break;
+ default:
+ if (oldPort.OvsPortName != newPort.OvsPortName ||
+ oldPort.SwitchId != newPort.SwitchId)
+ {
+ if (IsOvsConnected(oldPort))
+ {
+ DeleteOvsPort(e.OldValue);
+ }
+ if (IsOvsConnected(newPort))
+ {
+ LinkOrCreateOvsPort(e.NewValue);
+ }
+ }
+ // Currently adapter with same UUID cannot be migrated
+ // to another VM on Hyper-V. Ignoring the VM ID change case.
+ break;
+ }
+ }
+ finally
+ {
+ VirtualAdapterMonitor.Instance.ExitReadLock();
+ OvsSwitchMonitor.Instance.ExitReadLock();
+ }
+ }
+
+ private void OvsSwitchMonitor_ItemUpdated(object sender, WmiMonitorEventArgs<OvsSwitch> e)
+ {
+ OvsSwitchMonitor.Instance.EnterReadLock();
+ VirtualAdapterMonitor.Instance.EnterReadLock();
+ try
+ {
+ switch (e.Operation)
+ {
+ case ItemOperation.Add:
+ // New port is not connected to an OVS switch, ignoring it.
+ foreach (var port in VirtualAdapterMonitor.Instance.ItemsMap.Values)
+ {
+ if (port.SwitchId == e.NewValue.Id)
+ {
+ LinkOrCreateOvsPort(port);
+ }
+ }
+ break;
+ case ItemOperation.Remove:
+ foreach (var port in VirtualAdapterMonitor.Instance.ItemsMap.Values)
+ {
+ if (port.SwitchId == e.OldValue.Id)
+ {
+ DeleteOvsPort(port);
+ }
+ }
+ break;
+ default:
+ Trace.Assert(false, string.Format("Unhandled modification of switch {0} -> {1} detected",
+ e.OldValue, e.NewValue));
+ break;
+ }
+
+ }
+ finally
+ {
+ VirtualAdapterMonitor.Instance.ExitReadLock();
+ OvsSwitchMonitor.Instance.ExitReadLock();
+ }
+ }
+
+ public void Stop()
+ {
+ OvsSwitchMonitor.Instance.Unsubscribe();
+ VirtualAdapterMonitor.Instance.Unsubscribe();
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,107 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Management;
+using System.Threading;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+
+
+namespace OvsDiscoveryAgent
+{
+ public class VirtualAdapterMonitor : WmiMonitor<VirtualAdapter>
+ {
+ #region Consts
+ private static readonly string virtualAdapterDefaultElementName = "Dynamic Ethernet Switch Port";
+ private static readonly string switchNamePattern = "Msvm_VirtualEthernetSwitch.CreationClassName=\"Msvm_VirtualEthernetSwitch\",Name=\"([A-Za-z0-9\\-]+)\"";
+ private static readonly string virtualAdapterCaption = "Ethernet Connection Settings";
+ public static WqlCondition isVirtualAdapterCondition = new WqlBasicCondition(WqlColumns.Caption, virtualAdapterCaption);
+ public static readonly WqlCondition isEnabledCondition = new WqlBasicCondition<int>(WqlColumns.EnabledState, (int)EnabledState.Enabled);
+ #endregion
+ #region Fields
+ private static VirtualAdapterMonitor monitorInstance;
+ #endregion
+ protected VirtualAdapterMonitor() { }
+ public static VirtualAdapterMonitor Instance
+ {
+ get
+ {
+ if (monitorInstance == null)
+ {
+ monitorInstance = new VirtualAdapterMonitor();
+ }
+ return monitorInstance;
+ }
+ }
+ protected override ManagementObjectCollection QueryItems()
+ {
+ return WqlHelper.QueryAll(WqlTables.EthernetPortAllocationSettingData,
+ isVirtualAdapterCondition & isEnabledCondition);
+ }
+
+ protected override ManagementEventWatcher CreateEventWatcher()
+ {
+ return WqlHelper.GetEventWatcher(
+ WqlEventType.Operation, WqlTables.EthernetPortAllocationSettingData,
+ isVirtualAdapterCondition);
+ }
+
+ protected override string GetItemId(VirtualAdapter item)
+ {
+ return item.Id;
+ }
+
+ protected override VirtualAdapter ConvertItem(ManagementBaseObject mbo)
+ {
+ if (mbo == null) return null;
+ var o = mbo[WqlColumns.EnabledState];
+ if (((EnabledState)(ushort)o) != EnabledState.Enabled) return null;
+ VirtualAdapter result = new VirtualAdapter();
+ o = mbo[WqlColumns.InstanceID];
+ if (o != null)
+ {
+ string[] ids = o.ToString().Split(':', '\\');
+ if (ids.Length < 3)
+ {
+ Trace.TraceError("Unable to parse virutal adapter ID: {0}", o);
+ }
+ else
+ {
+ result.VmId = ids[1];
+ result.Id = ids[2];
+ }
+ }
+ o = mbo[WqlColumns.ElementName];
+ if (o != null)
+ {
+ var name = o.ToString();
+ if (name != virtualAdapterDefaultElementName)
+ {
+ result.OvsPortName = name;
+ }
+ }
+ var resources = mbo[WqlColumns.HostResource] as string[];
+ if (resources != null)
+ {
+ foreach (string res in resources)
+ {
+ Match match = Regex.Match(res, switchNamePattern, RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ result.SwitchId = match.Groups[1].Value;
+ }
+ }
+ }
+ result.SourceWmiObject = mbo;
+ return result;
+ }
+
+ protected override bool IsSameItem(VirtualAdapter oldItem, VirtualAdapter newItem)
+ {
+ bool oldEnabled = (oldItem != null);
+ bool newEnabled = (newItem != null);
+ if (oldEnabled != newEnabled) return false;
+ if (oldEnabled && !oldItem.Equals(newItem)) return false;
+ return true;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.Management;
+using System.Threading;
+using System.Diagnostics;
+
+namespace OvsDiscoveryAgent
+{
+ public enum ItemOperation
+ {
+ Add,
+ Remove,
+ Modify
+ }
+ public class WmiMonitorEventArgs<T> : EventArgs
+ {
+ public T OldValue { get; set; }
+ public T NewValue { get; set; }
+ public ItemOperation Operation { get; set; }
+ public ManagementBaseObject NewEvent { get; set; }
+ }
+ public delegate void WmiMonitorEventHandler<T>(object sender, WmiMonitorEventArgs<T> e);
+ public abstract class WmiMonitor<T> where T : class
+ {
+ protected ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
+ public Dictionary<string, T> ItemsMap { get; private set; }
+ protected WmiMonitor()
+ {
+ ItemsMap = new Dictionary<string, T>();
+ }
+ public event WmiMonitorEventHandler<T> ItemUpdated;
+ protected abstract ManagementObjectCollection QueryItems();
+ protected abstract ManagementEventWatcher CreateEventWatcher();
+ protected abstract string GetItemId(T item);
+ protected abstract T ConvertItem(ManagementBaseObject mbo);
+ public void EnterReadLock()
+ {
+ cacheLock.EnterReadLock();
+ }
+ public void ExitReadLock()
+ {
+ cacheLock.ExitReadLock();
+ }
+ public bool Contains(string id)
+ {
+ bool needLock = !cacheLock.IsReadLockHeld;
+ if (needLock)
+ {
+ cacheLock.EnterReadLock();
+ }
+ try
+ {
+ return ItemsMap.ContainsKey(id);
+ }
+ finally
+ {
+ if (needLock)
+ {
+ cacheLock.ExitReadLock();
+ }
+ }
+ }
+ private bool AddOrDeleteItem(T item, bool isAdd)
+ {
+ if (item == null)
+ {
+ Trace.TraceError("Item {0} is null", typeof(T));
+ return false;
+ }
+ string id = GetItemId(item);
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ Trace.TraceError("Cannot find ID for {0}", item);
+ return false;
+ }
+ if (isAdd)
+ {
+ if (ItemsMap.ContainsKey(id))
+ {
+ Trace.TraceError("Duplicate ID {0} found for {1}", id,
+ typeof(T));
+ return false;
+ }
+ else
+ {
+ ItemsMap.Add(id, item);
+ return true;
+ }
+ }
+ else
+ {
+ if (!ItemsMap.ContainsKey(id))
+ {
+ Trace.TraceError("Unable to find {1} that has ID {0}", id,
+ typeof(T));
+ return false;
+ }
+ else
+ {
+ ItemsMap.Remove(id);
+ return true;
+ }
+ }
+ }
+ public void LoadAllItems()
+ {
+ var result = QueryItems();
+ cacheLock.EnterWriteLock();
+ try
+ {
+ ItemsMap.Clear();
+ foreach (var mo in result)
+ {
+ AddOrDeleteItem(ConvertItem(mo), true);
+ }
+ }
+ finally
+ {
+ cacheLock.ExitWriteLock();
+ }
+ }
+
+ public void Subscribe()
+ {
+ if (EventWatcher != null) return;
+ EventWatcher = CreateEventWatcher();
+ EventWatcher.EventArrived += EventWatcher_EventArrived;
+ EventWatcher.Start();
+ }
+
+ public void Unsubscribe()
+ {
+ if (EventWatcher == null) return;
+ EventWatcher.EventArrived -= EventWatcher_EventArrived;
+ EventWatcher.Stop();
+ EventWatcher = null;
+ }
+
+ protected abstract bool IsSameItem(T oldItem, T newItem);
+
+ private void EventWatcher_EventArrived(object sender, EventArrivedEventArgs e)
+ {
+ var changed = WqlHelper.ParseEventArrivedEventArgs(e);
+ T oldItem = default(T), newItem = default(T);
+ if (changed.Item1 != null)
+ {
+ oldItem = ConvertItem(changed.Item1);
+ }
+ if (changed.Item2 != null)
+ {
+ newItem = ConvertItem(changed.Item2);
+ }
+ if (IsSameItem(oldItem, newItem))
+ {
+ //Nothing relevant is changed.
+ return;
+ }
+ var args = new WmiMonitorEventArgs<T>();
+ bool updateResult;
+ args.NewEvent = e.NewEvent;
+ args.OldValue = oldItem;
+ args.NewValue = newItem;
+ cacheLock.EnterWriteLock();
+ try
+ {
+ if (!IsSameItem(oldItem, null) && IsSameItem(newItem, null))
+ {
+ args.Operation = ItemOperation.Remove;
+ updateResult = AddOrDeleteItem(oldItem, false);
+ }
+ else if (IsSameItem(oldItem, null) && !IsSameItem(newItem, null))
+ {
+ args.Operation = ItemOperation.Add;
+ updateResult = AddOrDeleteItem(newItem, true);
+ }
+ else
+ {
+ args.Operation = ItemOperation.Modify;
+ updateResult = AddOrDeleteItem(oldItem, false) & AddOrDeleteItem(newItem, true);
+ }
+ }
+ finally
+ {
+ cacheLock.ExitWriteLock();
+ }
+ if (updateResult)
+ {
+ ItemUpdated.Invoke(this, args);
+ }
+ }
+ protected ManagementEventWatcher EventWatcher { get; set; }
+ }
+}
new file mode 100644
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace OvsDiscoveryAgent
+{
+ public enum WqlLogicalOperator
+ {
+ AND,
+ OR
+ }
+ public enum WqlValueOperator
+ {
+ Equal,
+ NotEqual,
+ InstanceOf,
+ Like
+ }
+ public abstract class WqlCondition
+ {
+ public static WqlCondition operator &(WqlCondition cond1, WqlCondition cond2)
+ {
+ return new WqlLogicalCondition(cond1, WqlLogicalOperator.AND, cond2);
+ }
+ public static WqlCondition operator |(WqlCondition cond1, WqlCondition cond2)
+ {
+ return new WqlLogicalCondition(cond1, WqlLogicalOperator.OR, cond2);
+ }
+ public abstract string ToString(string owner);
+ }
+ public class WqlAlwaysTrueCondition : WqlCondition
+ {
+ public override string ToString()
+ {
+ return string.Empty;
+ }
+ public override string ToString(string owner)
+ {
+ return string.Empty;
+ }
+ }
+ public class UserDefinedCondition : WqlCondition
+ {
+ public UserDefinedCondition(string condition)
+ {
+ Condition = condition;
+ }
+
+ public string Condition { get; set; }
+ public override string ToString()
+ {
+ return Condition;
+ }
+ public override string ToString(string owner)
+ {
+ return Condition;
+ }
+ }
+ public class WqlLogicalCondition : WqlCondition
+ {
+ public WqlLogicalCondition(WqlCondition cond1, WqlLogicalOperator oper, WqlCondition cond2)
+ {
+ Condition1 = cond1;
+ Condition2 = cond2;
+ Operator = oper;
+ }
+ public WqlLogicalOperator Operator { get; set; }
+ public WqlCondition Condition1 { get; set; }
+ public WqlCondition Condition2 { get; set; }
+ private string ConvertConditionToString(WqlCondition cond, string owner)
+ {
+ if (owner == null)
+ {
+ return (cond is WqlLogicalCondition) ?
+ string.Format("({0})", cond) :
+ cond.ToString();
+ }
+ else
+ {
+ return (cond is WqlLogicalCondition) ?
+ string.Format("({0})", cond.ToString(owner)) :
+ cond.ToString(owner);
+ }
+ }
+ public override string ToString()
+ {
+ return ToString(null);
+ }
+ public override string ToString(string owner)
+ {
+ var condStrs = new List<string>();
+ var condStr1 = ConvertConditionToString(Condition1, owner);
+ var condStr2 = ConvertConditionToString(Condition2, owner);
+ if (!string.IsNullOrWhiteSpace(condStr1)) condStrs.Add(condStr1);
+ if (!string.IsNullOrWhiteSpace(condStr2)) condStrs.Add(condStr2);
+ return string.Join(Operator == WqlLogicalOperator.AND ? " AND " : " OR ",
+ condStrs);
+ }
+ }
+ public class WqlBasicCondition<T> : WqlCondition
+ {
+ public WqlBasicCondition() { }
+ public WqlBasicCondition(string key, T value)
+ {
+ Key = key;
+ Value = value;
+ Operator = WqlValueOperator.Equal;
+ }
+ public WqlBasicCondition(string key, WqlValueOperator oper, T value)
+ {
+ Key = key;
+ Value = value;
+ Operator = oper;
+ }
+ public WqlValueOperator Operator { get; set; }
+ public string Key { get; set; }
+ public T Value { get; set; }
+ public static string ToString(WqlValueOperator oper)
+ {
+ switch (oper)
+ {
+ case WqlValueOperator.Equal:
+ return "=";
+ case WqlValueOperator.InstanceOf:
+ return "ISA";
+ case WqlValueOperator.Like:
+ return "LIKE";
+ case WqlValueOperator.NotEqual:
+ return "<>";
+ default:
+ throw new NotImplementedException("Unsupported operator: " + oper);
+ }
+ }
+
+ public override string ToString()
+ {
+ return ToString(null);
+ }
+ public override string ToString(string owner)
+ {
+ string key = (string.IsNullOrWhiteSpace(owner) ? string.Empty : owner + ".") + Key;
+ if (Value is string || Value.ToString().Contains(' '))
+ {
+ return string.Format("{0} {1} '{2}'", key, ToString(Operator), Value);
+ }
+ else
+ {
+ return string.Join(" ", key, ToString(Operator), Value);
+ }
+ }
+ }
+ public class WqlBasicCondition : WqlBasicCondition<string>
+ {
+ public WqlBasicCondition() { }
+ public WqlBasicCondition(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ Operator = WqlValueOperator.Equal;
+ }
+ public WqlBasicCondition(string key, WqlValueOperator oper, string value)
+ {
+ Key = key;
+ Value = value;
+ Operator = oper;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,132 @@
+using System;
+using System.Management;
+using System.Diagnostics;
+
+namespace OvsDiscoveryAgent
+{
+ public class WqlHelper
+ {
+ private static readonly string selectFromQuery = "SELECT {0} FROM {1}";
+ private static readonly string selectFromWhereQuery = "SELECT {0} FROM {1} WHERE {2}";
+ public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName)
+ {
+ return GetEventWatcher(eventType, tableName, new WqlAlwaysTrueCondition());
+ }
+ public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName,
+ string additionalConditions)
+ {
+ return GetEventWatcher(eventType, tableName, new UserDefinedCondition(additionalConditions));
+ }
+ public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName,
+ WqlCondition additionalCondition)
+ {
+ var scope = new ManagementScope(tableName.ScopeName);
+ scope.Connect();
+ var condTargetIsTable = new WqlBasicCondition(WqlColumns.TargetInstance, WqlValueOperator.InstanceOf,
+ tableName.Name);
+ var query = new WqlEventQuery(eventType.Name,
+ TimeSpan.FromSeconds(1),
+ (condTargetIsTable & new UserDefinedCondition(
+ additionalCondition.ToString(WqlColumns.TargetInstance))).ToString());
+ return new ManagementEventWatcher(scope, query);
+ }
+ public static ManagementObjectCollection QueryAll(WqlTable tableName, WqlCondition condition = null)
+ {
+ if (condition == null) { condition = new WqlAlwaysTrueCondition(); }
+ return QueryAll(tableName, condition.ToString(), null);
+ }
+ public static ManagementObjectCollection QueryAll(WqlTable tableName, string condition)
+ {
+ return QueryAll(tableName, condition, null);
+ }
+ public static ManagementObjectCollection QueryAll(WqlTable tableName, WqlCondition condition, string[] fields)
+ {
+ return QueryAll(tableName, condition.ToString(), fields);
+ }
+ public static ManagementObjectCollection QueryAll(WqlTable tableName, string condition, string[] fields)
+ {
+ string fieldStr = (fields == null || fields.Length == 0) ? "*" : string.Join(",", fields);
+ string queryStr;
+ if (string.IsNullOrWhiteSpace(condition))
+ {
+ queryStr = string.Format(selectFromQuery, fieldStr, tableName.Name);
+ }
+ else
+ {
+ queryStr = string.Format(selectFromWhereQuery, fieldStr, tableName.Name, condition);
+ }
+ var searcher = new ManagementObjectSearcher(tableName.ScopeName, queryStr);
+ return searcher.Get();
+ }
+
+ /// <summary>
+ /// Common utility function to get a service object
+ /// </summary>
+ /// <param name="tableName">Serivce object name</param>
+ /// <param name="scope">Output parameter</param>
+ /// <returns>Service object with the given name.</returns>
+ public static ManagementObject GetServiceObject(WqlTable tableName, out ManagementScope scope)
+ {
+ scope = new ManagementScope(tableName.ScopeName);
+ scope.Connect();
+ ManagementPath wmiPath = new ManagementPath(tableName.Name);
+ ManagementClass serviceClass = new ManagementClass(scope, wmiPath, null);
+ ManagementObjectCollection services = serviceClass.GetInstances();
+
+ ManagementObject serviceObject = null;
+
+ foreach (ManagementObject service in services)
+ {
+ serviceObject = service;
+ }
+ return serviceObject;
+ }
+ public static Tuple<ManagementBaseObject, ManagementBaseObject> ParseEventArrivedEventArgs(EventArrivedEventArgs args)
+ {
+ ManagementBaseObject oldMbo = null;
+ ManagementBaseObject newMbo = null;
+ if (WqlEventType.Modification == args)
+ {
+ oldMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.PreviousInstance];
+ newMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance];
+ }
+ else if (WqlEventType.Creation == args)
+ {
+ newMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance];
+ }
+ else if (WqlEventType.Deletion == args)
+ {
+ oldMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance];
+ }
+ return new Tuple<ManagementBaseObject, ManagementBaseObject>(oldMbo, newMbo);
+ }
+ public static bool JobCompleted(ManagementBaseObject outParams, ManagementScope scope)
+ {
+ bool jobCompleted = true;
+
+ //Retrieve msvc_StorageJob path. This is a full wmi path
+ string jobPath = (string)outParams["Job"];
+ ManagementObject job = new ManagementObject(scope, new ManagementPath(jobPath), null);
+ //Try to get storage job information
+ job.Get();
+ while ((UInt16)job["JobState"] == JobState.Starting
+ || (UInt16)job["JobState"] == JobState.Running)
+ {
+ Trace.TraceInformation("In progress... {0}% completed.", job["PercentComplete"]);
+ System.Threading.Thread.Sleep(1000);
+ job.Get();
+ }
+
+ //Figure out if job failed
+ UInt16 jobState = (UInt16)job["JobState"];
+ if (jobState != JobState.Completed)
+ {
+ UInt16 jobErrorCode = (UInt16)job["ErrorCode"];
+ Trace.TraceInformation("Error Code:{0}", jobErrorCode);
+ Trace.TraceInformation("ErrorDescription: {0}", (string)job["ErrorDescription"]);
+ jobCompleted = false;
+ }
+ return jobCompleted;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,133 @@
+using System;
+using System.Management;
+
+namespace OvsDiscoveryAgent
+{
+ public static class WqlColumns
+ {
+ public static readonly string SystemName = "SystemName";
+ public static readonly string EnabledState = "EnabledState";
+ public static readonly string ElementName = "ElementName";
+ public static readonly string HostResource = "HostResource";
+ public static readonly string InstanceID = "InstanceID";
+ public static readonly string Caption = "Caption";
+ public static readonly string TargetInstance = "TargetInstance";
+ public static readonly string PreviousInstance = "PreviousInstance";
+ }
+ public static class WqlMethods
+ {
+ public static readonly string ModifyResourceSettings = "ModifyResourceSettings";
+ }
+ public class WqlObject
+ {
+ public string Name { get; private set; }
+ public WqlObject() { }
+ public WqlObject(string type)
+ {
+ Name = type;
+ }
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+ public class WqlEventType : WqlObject
+ {
+ public WqlEventType() { }
+ public WqlEventType(string type) : base(type) { }
+ public static bool operator ==(WqlEventType type, ManagementBaseObject mbo)
+ {
+ return type.Name == mbo.ClassPath.ClassName;
+ }
+ public static bool operator !=(WqlEventType type, ManagementBaseObject mbo)
+ {
+ return !(type == mbo);
+ }
+ public static bool operator == (WqlEventType type, EventArrivedEventArgs args)
+ {
+ return type == args.NewEvent;
+ }
+ public static bool operator !=(WqlEventType type, EventArrivedEventArgs args)
+ {
+ return !(type == args);
+ }
+ public override int GetHashCode()
+ {
+ return Name.GetHashCode();
+ }
+ public override bool Equals(object obj)
+ {
+ return Name == obj.ToString();
+ }
+ public static readonly WqlEventType Operation = new WqlEventType("__InstanceOperationEvent");
+ public static readonly WqlEventType Modification = new WqlEventType("__InstanceModificationEvent");
+ public static readonly WqlEventType Creation = new WqlEventType("__InstanceCreationEvent");
+ public static readonly WqlEventType Deletion = new WqlEventType("__InstanceDeletionEvent");
+ }
+ public class WqlTable : WqlObject
+ {
+ public WqlTable() { }
+ public WqlTable(string scopeName, string tableName) : base(tableName)
+ {
+ ScopeName = scopeName;
+ }
+ public override string ToString()
+ {
+ return string.Format("{0}:{1}", ScopeName, Name);
+ }
+ public string ScopeName { get; private set; }
+ }
+
+ public static class WqlTables
+ {
+ private static readonly string virtScope = @"\root\virtualization\v2";
+ public static readonly WqlTable SyntheticEthernetPort = new WqlTable(virtScope, "Msvm_SyntheticEthernetPort");
+ public static readonly WqlTable EthernetSwitchExtension = new WqlTable(virtScope, "Msvm_EthernetSwitchExtension");
+ public static readonly WqlTable EthernetPortAllocationSettingData = new WqlTable(virtScope, "Msvm_EthernetPortAllocationSettingData");
+ public static readonly WqlTable VirtualSystemManagementService = new WqlTable(virtScope, "Msvm_VirtualSystemManagementService");
+ }
+
+ public enum EnabledState
+ {
+ Unknown = 0,
+ Enabled = 2,
+ Disabled = 3,
+ Paused = 32768,
+ Suspended = 32769,
+ Starting = 32770,
+ Snapshotting = 32771,
+ Saving = 32773,
+ Stopping = 32774,
+ Pausing = 32776,
+ Resuming = 32777,
+ }
+ public static class ReturnCode
+ {
+ public const UInt32 Completed = 0;
+ public const UInt32 Started = 4096;
+ public const UInt32 Failed = 32768;
+ public const UInt32 AccessDenied = 32769;
+ public const UInt32 NotSupported = 32770;
+ public const UInt32 Unknown = 32771;
+ public const UInt32 Timeout = 32772;
+ public const UInt32 InvalidParameter = 32773;
+ public const UInt32 SystemInUse = 32774;
+ public const UInt32 InvalidState = 32775;
+ public const UInt32 IncorrectDataType = 32776;
+ public const UInt32 SystemNotAvailable = 32777;
+ public const UInt32 OutofMemory = 32778;
+ }
+ public static class JobState
+ {
+ public const UInt16 New = 2;
+ public const UInt16 Starting = 3;
+ public const UInt16 Running = 4;
+ public const UInt16 Suspended = 5;
+ public const UInt16 ShuttingDown = 6;
+ public const UInt16 Completed = 7;
+ public const UInt16 Terminated = 8;
+ public const UInt16 Killed = 9;
+ public const UInt16 Exception = 10;
+ public const UInt16 Service = 11;
+ }
+}
@@ -55,3 +55,24 @@ EXTRA_DIST += \
windows/ovs-windows-installer/images/dlgbmp.bmp \
windows/ovs-windows-installer/ovs-windows-installer.wixproj
+ovs_discovery_agent: all
+ MSBuild.exe windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln /target:Build /property:Configuration="Release"
+
+EXTRA_DIST += \
+ windows/OvsDiscoveryAgent/App.config \
+ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj \
+ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln \
+ windows/OvsDiscoveryAgent/OvsDiscoveryService.cs \
+ windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs \
+ windows/OvsDiscoveryAgent/OvsVsctl.cs \
+ windows/OvsDiscoveryAgent/Program.cs \
+ windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs \
+ windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs \
+ windows/OvsDiscoveryAgent/Properties/Settings.settings \
+ windows/OvsDiscoveryAgent/VirtualAdapter.cs \
+ windows/OvsDiscoveryAgent/VirtualAdapterManager.cs \
+ windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs \
+ windows/OvsDiscoveryAgent/WmiMonitor.cs \
+ windows/OvsDiscoveryAgent/WqlCondition.cs \
+ windows/OvsDiscoveryAgent/WqlHelper.cs \
+ windows/OvsDiscoveryAgent/WqlObject.cs
Signed-off-by: Yin Lin <linyi@vmware.com> --- windows/OvsDiscoveryAgent/App.config | 18 ++ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj | 83 ++++++++ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln | 34 +++ windows/OvsDiscoveryAgent/OvsDiscoveryService.cs | 67 ++++++ windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs | 73 +++++++ windows/OvsDiscoveryAgent/OvsVsctl.cs | 84 ++++++++ windows/OvsDiscoveryAgent/Program.cs | 26 +++ .../OvsDiscoveryAgent/Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Settings.Designer.cs | 38 ++++ .../OvsDiscoveryAgent/Properties/Settings.settings | 9 + windows/OvsDiscoveryAgent/VirtualAdapter.cs | 36 ++++ windows/OvsDiscoveryAgent/VirtualAdapterManager.cs | 228 +++++++++++++++++++++ windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs | 107 ++++++++++ windows/OvsDiscoveryAgent/WmiMonitor.cs | 193 +++++++++++++++++ windows/OvsDiscoveryAgent/WqlCondition.cs | 168 +++++++++++++++ windows/OvsDiscoveryAgent/WqlHelper.cs | 132 ++++++++++++ windows/OvsDiscoveryAgent/WqlObject.cs | 133 ++++++++++++ windows/automake.mk | 21 ++ 18 files changed, 1486 insertions(+) create mode 100644 windows/OvsDiscoveryAgent/App.config create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryService.cs create mode 100644 windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/OvsVsctl.cs create mode 100644 windows/OvsDiscoveryAgent/Program.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/Settings.settings create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapter.cs create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapterManager.cs create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/WmiMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/WqlCondition.cs create mode 100644 windows/OvsDiscoveryAgent/WqlHelper.cs create mode 100644 windows/OvsDiscoveryAgent/WqlObject.cs