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

Shashank Ram rams at vmware.com
Fri Dec 23 21:38:47 UTC 2016


Hi Yin, thanks for your changes. Would it be possible for you to split this feature into smaller, easier to review commits?


Also, please also mention in the commit message how you tested out different scenarios, and what will be the impact on existing Vif addition workflow etc.


Thanks!

________________________________
From: ovs-dev-bounces at openvswitch.org <ovs-dev-bounces at openvswitch.org> on behalf of Yin Lin <linyi at vmware.com>
Sent: Monday, December 12, 2016 2:20:51 PM
To: dev at openvswitch.org
Subject: [ovs-dev] [PATCH v2] Windows: Implement Hyper-V VIF discovery agent.

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 ++++++++++++
 windows/automake.mk                                |  21 ++
 18 files changed, 1484 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="https://urldefense.proofpoint.com/v2/url?u=http-3A__schemas.microsoft.com_developer_msbuild_2003&d=DgIFaQ&c=uilaK90D4TOVoH58JNXRgQ&r=6OuVHk-mnufSWzkKa74UkQ&m=1xklhz_AUTMCHdJjqAkgoYBWaBfNudcFfaNpo8CBYU8&s=k3L9MzpvSg_ihGUKIhnwvJN2YQVsSq8COkkuku2vLlg&e= ">
+  <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="https://urldefense.proofpoint.com/v2/url?u=http-3A__schemas.microsoft.com_VisualStudio_2004_01_settings&d=DgIFaQ&c=uilaK90D4TOVoH58JNXRgQ&r=6OuVHk-mnufSWzkKa74UkQ&m=1xklhz_AUTMCHdJjqAkgoYBWaBfNudcFfaNpo8CBYU8&s=dCDCFCjpEjKRL_AfYIMa1LsY_Xeu3FqeOh3q_FWdhPY&e= " 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;
+    }
+}
diff --git a/windows/automake.mk b/windows/automake.mk
index fa610ec..a003c30 100644
--- a/windows/automake.mk
+++ b/windows/automake.mk
@@ -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
--
2.8.0.windows.1



More information about the dev mailing list