Signed-off-by: Yin Lin <[email protected]> --- windows/OvsDiscoveryAgent/App.config | 18 ++ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj | 83 ++++++++ windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln | 34 +++ windows/OvsDiscoveryAgent/OvsDiscoveryService.cs | 67 ++++++ windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs | 73 +++++++ windows/OvsDiscoveryAgent/OvsVsctl.cs | 84 ++++++++ windows/OvsDiscoveryAgent/Program.cs | 26 +++ .../OvsDiscoveryAgent/Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Settings.Designer.cs | 38 ++++ .../OvsDiscoveryAgent/Properties/Settings.settings | 9 + windows/OvsDiscoveryAgent/VirtualAdapter.cs | 36 ++++ windows/OvsDiscoveryAgent/VirtualAdapterManager.cs | 228 +++++++++++++++++++++ windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs | 107 ++++++++++ windows/OvsDiscoveryAgent/WmiMonitor.cs | 193 +++++++++++++++++ windows/OvsDiscoveryAgent/WqlCondition.cs | 168 +++++++++++++++ windows/OvsDiscoveryAgent/WqlHelper.cs | 132 ++++++++++++ windows/OvsDiscoveryAgent/WqlObject.cs | 133 ++++++++++++ windows/automake.mk | 21 ++ 18 files changed, 1486 insertions(+) create mode 100644 windows/OvsDiscoveryAgent/App.config create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryAgent.csproj create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryAgent.sln create mode 100644 windows/OvsDiscoveryAgent/OvsDiscoveryService.cs create mode 100644 windows/OvsDiscoveryAgent/OvsSwitchMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/OvsVsctl.cs create mode 100644 windows/OvsDiscoveryAgent/Program.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/AssemblyInfo.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/Settings.Designer.cs create mode 100644 windows/OvsDiscoveryAgent/Properties/Settings.settings create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapter.cs create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapterManager.cs create mode 100644 windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/WmiMonitor.cs create mode 100644 windows/OvsDiscoveryAgent/WqlCondition.cs create mode 100644 windows/OvsDiscoveryAgent/WqlHelper.cs create mode 100644 windows/OvsDiscoveryAgent/WqlObject.cs
diff --git a/windows/OvsDiscoveryAgent/App.config b/windows/OvsDiscoveryAgent/App.config new file mode 100644 index 0000000..f2c7a15 --- /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" /> + </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..ee709d3 --- /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</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..2fe2c81 --- /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..da423fe --- /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..a04b579 --- /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 readonly WqlCondition isOvsExtensionCondition = new WqlBasicCondition(WqlColumns.ElementName, ovsExtentionName); + private static readonly WqlCondition isEnabledCondition = new WqlBasicCondition<int>(WqlColumns.EnabledState, (int)EnabledState.Enabled); + #endregion + protected OvsSwitchMonitor() { } + private static OvsSwitchMonitor monitorInstance; + public static OvsSwitchMonitor Instance + { + get + { + if (monitorInstance == null) + { + monitorInstance = new OvsSwitchMonitor(); + } + return monitorInstance; + } + } + + protected override ManagementObjectCollection QueryItems() + { + return WqlHelper.QueryAll(WqlTables.EthernetSwitchExtension, + isOvsExtensionCondition & isEnabledCondition, + new[] { WqlColumns.SystemName, WqlColumns.EnabledState }); + } + protected override string GetItemId(OvsSwitch item) + { + return item.Id; + } + + protected override OvsSwitch ConvertItem(ManagementBaseObject mbo) + { + if (mbo == null) return null; + if ((EnabledState)((ushort)mbo[WqlColumns.EnabledState]) != EnabledState.Enabled) + { + return null; + } + var result = new OvsSwitch(); + result.Id = mbo[WqlColumns.SystemName] as string; + return result; + } + + protected override bool IsSameItem(OvsSwitch oldItem, OvsSwitch newItem) + { + bool oldEnabled = (oldItem != null); + bool newEnabled = (newItem != null); + if (oldEnabled != newEnabled) return false; + if (oldEnabled && oldItem.Id != newItem.Id) return false; + return true; + } + + protected override ManagementEventWatcher CreateEventWatcher() + { + return WqlHelper.GetEventWatcher(WqlEventType.Operation, + WqlTables.EthernetSwitchExtension, + isOvsExtensionCondition); + } + } +} diff --git a/windows/OvsDiscoveryAgent/OvsVsctl.cs b/windows/OvsDiscoveryAgent/OvsVsctl.cs new file mode 100644 index 0000000..73a250e --- /dev/null +++ b/windows/OvsDiscoveryAgent/OvsVsctl.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OvsDiscoveryAgent +{ + /// <summary> + /// Provide basic ovs-vsctl functionality. + /// </summary> + /// <remarks> + /// TODO: This class assumes that ovs-vsctl is in the default PATH and calls + /// it as an external process. In its final format, this class should be a + /// wrapper around ovs-vsctl.c and built as a C++ CLR DLL project. + /// </remarks> + public static class OvsVsctl + { + private struct CommandResult + { + public int ExitCode; + public string OutputMessage; + public string ErrorMessage; + } + private static Process RunCommandAsync(string command, string args) + { + Trace.TraceInformation("[Console] {0} {1}", command, args); + Process process = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo + { + WindowStyle = ProcessWindowStyle.Hidden, + FileName = command, + Arguments = args, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + process.StartInfo = startInfo; + process.Start(); + return process; + } + private static CommandResult RunCommand(string command, string args) + { + int maxTimeoutMs = 1000; + var p = RunCommandAsync(command, args); + var result = new CommandResult(); + if (!p.WaitForExit(maxTimeoutMs)) + { + p.Kill(); + string errorMsg = string.Format("Command '{0} {1}' did not return within {2} milliseconds.", + command, args, maxTimeoutMs); + Trace.TraceError(errorMsg); + result.ExitCode = -1; + result.ErrorMessage = errorMsg; + } + else + { + result.ExitCode = p.ExitCode; + result.OutputMessage = p.StandardOutput.ReadToEnd(); + result.ErrorMessage = p.StandardError.ReadToEnd(); + Trace.TraceInformation("Command '{0} {1}' returns {2}, stdout='{3}', stderr='{4}'", + command, args, p.ExitCode, result.OutputMessage, result.ErrorMessage); + } + return result; + } + public static void AddPort(string bridge, string port, string[] settings, bool mayExist = true) + { + // Example: ovs-vsctl add-port <bridge> <port> -- set interface <port> external_ids:iface-id=<uuid> + // TODO: There is a bug in ovs kernel that result in "ovs-vsctl add-port" returning an error + // when VM has not been powered on. We are ignoring error for now. + RunCommand("ovs-vsctl.exe", string.Join(" ", mayExist?"--may-exist":string.Empty, + "add-port", bridge, port, string.Join(" ", settings))); + } + public static void DeletePort(string bridge, string port) + { + // Example: ovs-vsctl del-port <bridge> <port> + // TODO: There is a bug in ovs userspace that result in "ovs-vsctl del-port" always hanging forever + // when OVS extension has been disabled. This results in we always have to kill the process when + // we delete the ports connected to the disabled switch. + RunCommand("ovs-vsctl.exe", string.Format("del-port {0} {1}", bridge, port)); + } + } +} diff --git a/windows/OvsDiscoveryAgent/Program.cs b/windows/OvsDiscoveryAgent/Program.cs new file mode 100644 index 0000000..fe09bd8 --- /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..8b986ab --- /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..765f727 --- /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", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("ovs-int")] + public string OvsBridge { + get { + return ((string)(this["OvsBridge"])); + } + set { + this["OvsBridge"] = value; + } + } + } +} diff --git a/windows/OvsDiscoveryAgent/Properties/Settings.settings b/windows/OvsDiscoveryAgent/Properties/Settings.settings new file mode 100644 index 0000000..096d87e --- /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..7853ec3 --- /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..15ea79a --- /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 (outParams != null && ((UInt32)outParams["ReturnValue"] == ReturnCode.Started && WqlHelper.JobCompleted(outParams, scope) || + (UInt32)outParams["ReturnValue"] == ReturnCode.Completed)) + { + Trace.TraceInformation("Set OVS port {0} -> {1} completed successfully.", + adapter.OvsPortName, adapter.Id); + } + else + { + Trace.TraceError("Failed to set OVS port {0} -> {1}.", adapter.OvsPortName, adapter.Id); + } + } + + protected void LinkOrCreateOvsPort(VirtualAdapter newPort) + { + if (string.IsNullOrEmpty(newPort.OvsPortName)) + { + LinkOvsPort(newPort); + // Once we link OVS port a Modification event will be triggered. + // Ovs port will be added at that time. + } + else + { + // This handles the case where user add a Hyper-V VIF with OVS port + // specified. + CreateOvsPort(newPort); + } + } + + protected bool IsOvsConnected(VirtualAdapter adapter) + { + return OvsSwitchMonitor.Instance.Contains(adapter.SwitchId); + } + + private void VirtualAdapterMonitor_ItemUpdated(object sender, WmiMonitorEventArgs<VirtualAdapter> e) + { + OvsSwitchMonitor.Instance.EnterReadLock(); + VirtualAdapterMonitor.Instance.EnterReadLock(); + try + { + var newPort = e.NewValue; + var oldPort = e.OldValue; + switch (e.Operation) + { + case ItemOperation.Add: + // New port is not connected to an OVS switch, ignoring it. + if (IsOvsConnected(newPort)) + { + LinkOrCreateOvsPort(newPort); + } + break; + case ItemOperation.Remove: + if (IsOvsConnected(oldPort)) + { + DeleteOvsPort(oldPort); + } + break; + default: + if (oldPort.OvsPortName != newPort.OvsPortName || + oldPort.SwitchId != newPort.SwitchId) + { + if (IsOvsConnected(oldPort)) + { + DeleteOvsPort(e.OldValue); + } + if (IsOvsConnected(newPort)) + { + LinkOrCreateOvsPort(e.NewValue); + } + } + // Currently adapter with same UUID cannot be migrated + // to another VM on Hyper-V. Ignoring the VM ID change case. + break; + } + } + finally + { + VirtualAdapterMonitor.Instance.ExitReadLock(); + OvsSwitchMonitor.Instance.ExitReadLock(); + } + } + + private void OvsSwitchMonitor_ItemUpdated(object sender, WmiMonitorEventArgs<OvsSwitch> e) + { + OvsSwitchMonitor.Instance.EnterReadLock(); + VirtualAdapterMonitor.Instance.EnterReadLock(); + try + { + switch (e.Operation) + { + case ItemOperation.Add: + // New port is not connected to an OVS switch, ignoring it. + foreach (var port in VirtualAdapterMonitor.Instance.ItemsMap.Values) + { + if (port.SwitchId == e.NewValue.Id) + { + LinkOrCreateOvsPort(port); + } + } + break; + case ItemOperation.Remove: + foreach (var port in VirtualAdapterMonitor.Instance.ItemsMap.Values) + { + if (port.SwitchId == e.OldValue.Id) + { + DeleteOvsPort(port); + } + } + break; + default: + Trace.Assert(false, string.Format("Unhandled modification of switch {0} -> {1} detected", + e.OldValue, e.NewValue)); + break; + } + + } + finally + { + VirtualAdapterMonitor.Instance.ExitReadLock(); + OvsSwitchMonitor.Instance.ExitReadLock(); + } + } + + public void Stop() + { + OvsSwitchMonitor.Instance.Unsubscribe(); + VirtualAdapterMonitor.Instance.Unsubscribe(); + } + } +} diff --git a/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs b/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs new file mode 100644 index 0000000..0ae92f1 --- /dev/null +++ b/windows/OvsDiscoveryAgent/VirtualAdapterMonitor.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Threading; +using System.Diagnostics; +using System.Text.RegularExpressions; + + +namespace OvsDiscoveryAgent +{ + public class VirtualAdapterMonitor : WmiMonitor<VirtualAdapter> + { + #region Consts + private static readonly string virtualAdapterDefaultElementName = "Dynamic Ethernet Switch Port"; + private static readonly string switchNamePattern = "Msvm_VirtualEthernetSwitch.CreationClassName=\"Msvm_VirtualEthernetSwitch\",Name=\"([A-Za-z0-9\\-]+)\""; + private static readonly string virtualAdapterCaption = "Ethernet Connection Settings"; + public static WqlCondition isVirtualAdapterCondition = new WqlBasicCondition(WqlColumns.Caption, virtualAdapterCaption); + public static readonly WqlCondition isEnabledCondition = new WqlBasicCondition<int>(WqlColumns.EnabledState, (int)EnabledState.Enabled); + #endregion + #region Fields + private static VirtualAdapterMonitor monitorInstance; + #endregion + protected VirtualAdapterMonitor() { } + public static VirtualAdapterMonitor Instance + { + get + { + if (monitorInstance == null) + { + monitorInstance = new VirtualAdapterMonitor(); + } + return monitorInstance; + } + } + protected override ManagementObjectCollection QueryItems() + { + return WqlHelper.QueryAll(WqlTables.EthernetPortAllocationSettingData, + isVirtualAdapterCondition & isEnabledCondition); + } + + protected override ManagementEventWatcher CreateEventWatcher() + { + return WqlHelper.GetEventWatcher( + WqlEventType.Operation, WqlTables.EthernetPortAllocationSettingData, + isVirtualAdapterCondition); + } + + protected override string GetItemId(VirtualAdapter item) + { + return item.Id; + } + + protected override VirtualAdapter ConvertItem(ManagementBaseObject mbo) + { + if (mbo == null) return null; + var o = mbo[WqlColumns.EnabledState]; + if (((EnabledState)(ushort)o) != EnabledState.Enabled) return null; + VirtualAdapter result = new VirtualAdapter(); + o = mbo[WqlColumns.InstanceID]; + if (o != null) + { + string[] ids = o.ToString().Split(':', '\\'); + if (ids.Length < 3) + { + Trace.TraceError("Unable to parse virutal adapter ID: {0}", o); + } + else + { + result.VmId = ids[1]; + result.Id = ids[2]; + } + } + o = mbo[WqlColumns.ElementName]; + if (o != null) + { + var name = o.ToString(); + if (name != virtualAdapterDefaultElementName) + { + result.OvsPortName = name; + } + } + var resources = mbo[WqlColumns.HostResource] as string[]; + if (resources != null) + { + foreach (string res in resources) + { + Match match = Regex.Match(res, switchNamePattern, RegexOptions.IgnoreCase); + if (match.Success) + { + result.SwitchId = match.Groups[1].Value; + } + } + } + result.SourceWmiObject = mbo; + return result; + } + + protected override bool IsSameItem(VirtualAdapter oldItem, VirtualAdapter newItem) + { + bool oldEnabled = (oldItem != null); + bool newEnabled = (newItem != null); + if (oldEnabled != newEnabled) return false; + if (oldEnabled && !oldItem.Equals(newItem)) return false; + return true; + } + } +} diff --git a/windows/OvsDiscoveryAgent/WmiMonitor.cs b/windows/OvsDiscoveryAgent/WmiMonitor.cs new file mode 100644 index 0000000..f166553 --- /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)); + return false; + } + string id = GetItemId(item); + if (string.IsNullOrWhiteSpace(id)) + { + Trace.TraceError("Cannot find ID for {0}", item); + return false; + } + if (isAdd) + { + if (ItemsMap.ContainsKey(id)) + { + Trace.TraceError("Duplicate ID {0} found for {1}", id, + typeof(T)); + return false; + } + else + { + ItemsMap.Add(id, item); + return true; + } + } + else + { + if (!ItemsMap.ContainsKey(id)) + { + Trace.TraceError("Unable to find {1} that has ID {0}", id, + typeof(T)); + return false; + } + else + { + ItemsMap.Remove(id); + return true; + } + } + } + public void LoadAllItems() + { + var result = QueryItems(); + cacheLock.EnterWriteLock(); + try + { + ItemsMap.Clear(); + foreach (var mo in result) + { + AddOrDeleteItem(ConvertItem(mo), true); + } + } + finally + { + cacheLock.ExitWriteLock(); + } + } + + public void Subscribe() + { + if (EventWatcher != null) return; + EventWatcher = CreateEventWatcher(); + EventWatcher.EventArrived += EventWatcher_EventArrived; + EventWatcher.Start(); + } + + public void Unsubscribe() + { + if (EventWatcher == null) return; + EventWatcher.EventArrived -= EventWatcher_EventArrived; + EventWatcher.Stop(); + EventWatcher = null; + } + + protected abstract bool IsSameItem(T oldItem, T newItem); + + private void EventWatcher_EventArrived(object sender, EventArrivedEventArgs e) + { + var changed = WqlHelper.ParseEventArrivedEventArgs(e); + T oldItem = default(T), newItem = default(T); + if (changed.Item1 != null) + { + oldItem = ConvertItem(changed.Item1); + } + if (changed.Item2 != null) + { + newItem = ConvertItem(changed.Item2); + } + if (IsSameItem(oldItem, newItem)) + { + //Nothing relevant is changed. + return; + } + var args = new WmiMonitorEventArgs<T>(); + bool updateResult; + args.NewEvent = e.NewEvent; + args.OldValue = oldItem; + args.NewValue = newItem; + cacheLock.EnterWriteLock(); + try + { + if (!IsSameItem(oldItem, null) && IsSameItem(newItem, null)) + { + args.Operation = ItemOperation.Remove; + updateResult = AddOrDeleteItem(oldItem, false); + } + else if (IsSameItem(oldItem, null) && !IsSameItem(newItem, null)) + { + args.Operation = ItemOperation.Add; + updateResult = AddOrDeleteItem(newItem, true); + } + else + { + args.Operation = ItemOperation.Modify; + updateResult = AddOrDeleteItem(oldItem, false) & AddOrDeleteItem(newItem, true); + } + } + finally + { + cacheLock.ExitWriteLock(); + } + if (updateResult) + { + ItemUpdated.Invoke(this, args); + } + } + protected ManagementEventWatcher EventWatcher { get; set; } + } +} diff --git a/windows/OvsDiscoveryAgent/WqlCondition.cs b/windows/OvsDiscoveryAgent/WqlCondition.cs new file mode 100644 index 0000000..ae13b7a --- /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) : + 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..43461f7 --- /dev/null +++ b/windows/OvsDiscoveryAgent/WqlHelper.cs @@ -0,0 +1,132 @@ +using System; +using System.Management; +using System.Diagnostics; + +namespace OvsDiscoveryAgent +{ + public class WqlHelper + { + private static readonly string selectFromQuery = "SELECT {0} FROM {1}"; + private static readonly string selectFromWhereQuery = "SELECT {0} FROM {1} WHERE {2}"; + public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName) + { + return GetEventWatcher(eventType, tableName, new WqlAlwaysTrueCondition()); + } + public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName, + string additionalConditions) + { + return GetEventWatcher(eventType, tableName, new UserDefinedCondition(additionalConditions)); + } + public static ManagementEventWatcher GetEventWatcher(WqlEventType eventType, WqlTable tableName, + WqlCondition additionalCondition) + { + var scope = new ManagementScope(tableName.ScopeName); + scope.Connect(); + var condTargetIsTable = new WqlBasicCondition(WqlColumns.TargetInstance, WqlValueOperator.InstanceOf, + tableName.Name); + var query = new WqlEventQuery(eventType.Name, + TimeSpan.FromSeconds(1), + (condTargetIsTable & new UserDefinedCondition( + additionalCondition.ToString(WqlColumns.TargetInstance))).ToString()); + return new ManagementEventWatcher(scope, query); + } + public static ManagementObjectCollection QueryAll(WqlTable tableName, WqlCondition condition = null) + { + if (condition == null) { condition = new WqlAlwaysTrueCondition(); } + return QueryAll(tableName, condition.ToString(), null); + } + public static ManagementObjectCollection QueryAll(WqlTable tableName, string condition) + { + return QueryAll(tableName, condition, null); + } + public static ManagementObjectCollection QueryAll(WqlTable tableName, WqlCondition condition, string[] fields) + { + return QueryAll(tableName, condition.ToString(), fields); + } + public static ManagementObjectCollection QueryAll(WqlTable tableName, string condition, string[] fields) + { + string fieldStr = (fields == null || fields.Length == 0) ? "*" : string.Join(",", fields); + string queryStr; + if (string.IsNullOrWhiteSpace(condition)) + { + queryStr = string.Format(selectFromQuery, fieldStr, tableName.Name); + } + else + { + queryStr = string.Format(selectFromWhereQuery, fieldStr, tableName.Name, condition); + } + var searcher = new ManagementObjectSearcher(tableName.ScopeName, queryStr); + return searcher.Get(); + } + + /// <summary> + /// Common utility function to get a service object + /// </summary> + /// <param name="tableName">Serivce object name</param> + /// <param name="scope">Output parameter</param> + /// <returns>Service object with the given name.</returns> + public static ManagementObject GetServiceObject(WqlTable tableName, out ManagementScope scope) + { + scope = new ManagementScope(tableName.ScopeName); + scope.Connect(); + ManagementPath wmiPath = new ManagementPath(tableName.Name); + ManagementClass serviceClass = new ManagementClass(scope, wmiPath, null); + ManagementObjectCollection services = serviceClass.GetInstances(); + + ManagementObject serviceObject = null; + + foreach (ManagementObject service in services) + { + serviceObject = service; + } + return serviceObject; + } + public static Tuple<ManagementBaseObject, ManagementBaseObject> ParseEventArrivedEventArgs(EventArrivedEventArgs args) + { + ManagementBaseObject oldMbo = null; + ManagementBaseObject newMbo = null; + if (WqlEventType.Modification == args) + { + oldMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.PreviousInstance]; + newMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance]; + } + else if (WqlEventType.Creation == args) + { + newMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance]; + } + else if (WqlEventType.Deletion == args) + { + oldMbo = (ManagementBaseObject)args.NewEvent[WqlColumns.TargetInstance]; + } + return new Tuple<ManagementBaseObject, ManagementBaseObject>(oldMbo, newMbo); + } + public static bool JobCompleted(ManagementBaseObject outParams, ManagementScope scope) + { + bool jobCompleted = true; + + //Retrieve msvc_StorageJob path. This is a full wmi path + string jobPath = (string)outParams["Job"]; + ManagementObject job = new ManagementObject(scope, new ManagementPath(jobPath), null); + //Try to get storage job information + job.Get(); + while ((UInt16)job["JobState"] == JobState.Starting + || (UInt16)job["JobState"] == JobState.Running) + { + Trace.TraceInformation("In progress... {0}% completed.", job["PercentComplete"]); + System.Threading.Thread.Sleep(1000); + job.Get(); + } + + //Figure out if job failed + UInt16 jobState = (UInt16)job["JobState"]; + if (jobState != JobState.Completed) + { + UInt16 jobErrorCode = (UInt16)job["ErrorCode"]; + Trace.TraceInformation("Error Code:{0}", jobErrorCode); + Trace.TraceInformation("ErrorDescription: {0}", (string)job["ErrorDescription"]); + jobCompleted = false; + } + return jobCompleted; + } + } +} diff --git a/windows/OvsDiscoveryAgent/WqlObject.cs b/windows/OvsDiscoveryAgent/WqlObject.cs new file mode 100644 index 0000000..5676c69 --- /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 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
