[ovs-dev] Windows: Implement Hyper-V VIF discovery agent

Yin Lin yinlin10 at gmail.com
Mon Nov 21 20:07:55 UTC 2016


Create a VIF discovery daemon to tag Hyper-V adapters connected to OVS
switch
and add/delete OVS port correspondingly.

Signed-off-by: Yin Lin <linyi at 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              |  82 ++++++++
 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 | 109 ++++++++++
 windows/OvsDiscoveryAgent/WmiMonitor.cs            | 193 +++++++++++++++++
 windows/OvsDiscoveryAgent/WqlCondition.cs          | 168 +++++++++++++++
 windows/OvsDiscoveryAgent/WqlHelper.cs             | 130 ++++++++++++
 windows/OvsDiscoveryAgent/WqlObject.cs             | 133 ++++++++++++
 17 files changed, 1463 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

diff --git a/windows/OvsDiscoveryAgent/App.config
b/windows/OvsDiscoveryAgent/App.config
new file mode 100644
index 0000000..caf9613
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/App.config
@@ -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.2" />
+    </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
diff --git a/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj
b/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj
new file mode 100644
index 0000000..24c69bb
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj
@@ -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.2</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
diff --git a/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln
b/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln
new file mode 100644
index 0000000..b3d6371
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln
@@ -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
diff --git a/windows/OvsDiscoveryAgent/OvsDiscoveryService.cs
b/windows/OvsDiscoveryAgent/OvsDiscoveryService.cs
new file mode 100644
index 0000000..b41ecc8
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/OvsDiscoveryService.cs
@@ -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();
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs
b/windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs
new file mode 100644
index 0000000..77f1b7f
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs
@@ -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 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 string[] { 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);
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/OvsVsctl.cs
b/windows/OvsDiscoveryAgent/OvsVsctl.cs
new file mode 100644
index 0000000..3181b3b
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/OvsVsctl.cs
@@ -0,0 +1,82 @@
+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 System.Diagnostics.Process();
+            ProcessStartInfo startInfo = new
System.Diagnostics.ProcessStartInfo();
+            startInfo.WindowStyle =
System.Diagnostics.ProcessWindowStyle.Hidden;
+            startInfo.FileName = command;
+            startInfo.Arguments = args;
+            startInfo.RedirectStandardOutput = true;
+            startInfo.RedirectStandardError = true;
+            startInfo.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));
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/Program.cs
b/windows/OvsDiscoveryAgent/Program.cs
new file mode 100644
index 0000000..ec7fa65
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/Program.cs
@@ -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);
+            }
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs
b/windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..735e04c
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs
@@ -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")]
diff --git a/windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs
b/windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..42bbeed
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs
@@ -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",
"14.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;
+            }
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/Properties/Settings.settings
b/windows/OvsDiscoveryAgent/Properties/Settings.settings
new file mode 100644
index 0000000..73e48a6
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/Properties/Settings.settings
@@ -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
diff --git a/windows/OvsDiscoveryAgent/VirtualAdapter.cs
b/windows/OvsDiscoveryAgent/VirtualAdapter.cs
new file mode 100644
index 0000000..6b42e6d
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/VirtualAdapter.cs
@@ -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();
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/VirtualAdapterManager.cs
b/windows/OvsDiscoveryAgent/VirtualAdapterManager.cs
new file mode 100644
index 0000000..eac0b00
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/VirtualAdapterManager.cs
@@ -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 (((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();
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs
b/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs
new file mode 100644
index 0000000..b725b4e
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs
@@ -0,0 +1,109 @@
+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;
+        private ReaderWriterLockSlim adapterMapLock = new
ReaderWriterLockSlim();
+        private Dictionary<string, VirtualAdapter> adapterMap = new
Dictionary<string, VirtualAdapter>();
+        #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)
+            {
+                var ids = o.ToString().Split(':', '\\');
+                if (ids.Count() < 3)
+                {
+                    Trace.TraceError("Unable to parse virutal adapter ID:
{0}", o.ToString());
+                }
+                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;
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/WmiMonitor.cs
b/windows/OvsDiscoveryAgent/WmiMonitor.cs
new file mode 100644
index 0000000..5a59fb9
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/WmiMonitor.cs
@@ -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).ToString());
+                return false;
+            }
+            string id = GetItemId(item);
+            if (string.IsNullOrWhiteSpace(id))
+            {
+                Trace.TraceError("Cannot find ID for {0}",
item.ToString());
+                return false;
+            }
+            if (isAdd)
+            {
+                if (ItemsMap.ContainsKey(id))
+                {
+                    Trace.TraceError("Duplicate ID {0} found for {1}",
id.ToString(),
+                                     typeof(T).ToString());
+                    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.ToString(),
+                                     typeof(T).ToString());
+                    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; }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/WqlCondition.cs
b/windows/OvsDiscoveryAgent/WqlCondition.cs
new file mode 100644
index 0000000..1fac805
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/WqlCondition.cs
@@ -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.ToString()) :
+                   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;
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/WqlHelper.cs
b/windows/OvsDiscoveryAgent/WqlHelper.cs
new file mode 100644
index 0000000..c959c76
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/WqlHelper.cs
@@ -0,0 +1,130 @@
+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>
+        /// <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;
+        }
+    }
+}
diff --git a/windows/OvsDiscoveryAgent/WqlObject.cs
b/windows/OvsDiscoveryAgent/WqlObject.cs
new file mode 100644
index 0000000..148245f
--- /dev/null
+++ b/windows/OvsDiscoveryAgent/WqlObject.cs
@@ -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;
+    }
+}
-- 
2.8.0.windows.1


More information about the dev mailing list