Merge "Add Murano Agent for Windows"

This commit is contained in:
Jenkins 2015-06-05 16:47:43 +00:00 committed by Gerrit Code Review
commit 7b17e6d22e
22 changed files with 1333 additions and 0 deletions

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.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>{501BE151-4B8C-4355-88DC-3AEF1921B2D7}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Mirantis.Murano</RootNamespace>
<AssemblyName>ExecutionPlanGenerator</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</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="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</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>

View File

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Mirantis.Murano
{
class Command
{
public string Name { get; set; }
public Dictionary<string, object> Arguments { get; set; }
}
class ExecutionPlan
{
public List<string> Scripts { get; set; }
public List<Command> Commands { get; set; }
public int RebootOnCompletion { get; set; }
}
class Program
{
static void Main(string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
Console.WriteLine("Usage: ExecutionPlanGenerator inputfile [outputfile]");
return;
}
var outFile = args.Length == 2 ? args[1] : null;
var plan = new ExecutionPlan {
Scripts = new List<string>(),
Commands = new List<Command>()
};
var lines = File.ReadAllLines(args[0]);
foreach (var statement in lines
.Select(t => t.Split(new[] { ' ', '\t' }, 2))
.Where(t => t.Length == 2)
.Select(t => new Tuple<string, string>(t[0].Trim().ToLower(), t[1].Trim())))
{
switch (statement.Item1)
{
case "include":
Include(statement.Item2, plan, args[0]);
break;
case "call":
Call(statement.Item2, plan);
break;
case "reboot":
plan.RebootOnCompletion = int.Parse(statement.Item2);
break;
case "out":
if (args.Length < 2)
{
var path = statement.Item2;
if (!Path.IsPathRooted(path))
{
path = Path.Combine(Path.GetDirectoryName(args[0]), path);
}
outFile = path;
}
break;
}
}
var data = JsonConvert.SerializeObject(plan, Formatting.Indented);
if (outFile == null)
{
Console.WriteLine(data);
}
else
{
File.WriteAllText(outFile, data);
}
}
private static void Call(string line, ExecutionPlan plan)
{
var parts = line.Split(new[] { ' ', '\t'}, 2);
var command = new Command() {
Name = parts[0].Trim(),
Arguments = new Dictionary<string, object>()
};
if (parts.Length == 2)
{
foreach (var x in parts[1]
.Split(',')
.Select(t => t.Split('='))
.Where(t => t.Length == 2)
.Select(t => new KeyValuePair<string, string>(t[0].Trim(), t[1].Trim())))
{
object value = null;
long num;
bool boolean;
if (x.Value.StartsWith("\""))
{
value = x.Value.Substring(1, x.Value.Length - 2);
}
else if (long.TryParse(x.Value, out num))
{
value = num;
}
else if (bool.TryParse(x.Value, out boolean))
{
value = boolean;
}
else
{
continue;
}
command.Arguments.Add(x.Key, value);
}
}
plan.Commands.Add(command);
}
private static void Include(string file, ExecutionPlan plan, string dslPath)
{
var path = file;
if (!Path.IsPathRooted(file))
{
path = Path.Combine(Path.GetDirectoryName(dslPath), path);
}
var text = File.ReadAllText(path, Encoding.UTF8);
plan.Scripts.Add(Convert.ToBase64String(Encoding.UTF8.GetBytes(text)));
}
}
}

View File

@ -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("ConsoleApplication1")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ConsoleApplication1")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[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("9aab688a-ce5f-402e-8891-2d7b4ae85ea3")]
// 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")]

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
</packages>

View File

@ -0,0 +1,8 @@
Murano Windows Agent
====================
Murano Windows Agent is an initial version of Murano Agent.
Currently, it's outdated and not supported.
The main difference with the new Python agent is support of running Powershell.
After this support will be added to Python Agent, Windows Agent will be dropped.

Binary file not shown.

View File

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsAgent", "WindowsAgent\WindowsAgent.csproj", "{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecutionPlanGenerator", "ExecutionPlanGenerator\ExecutionPlanGenerator.csproj", "{501BE151-4B8C-4355-88DC-3AEF1921B2D7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}.Release|Any CPU.Build.0 = Release|Any CPU
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{501BE151-4B8C-4355-88DC-3AEF1921B2D7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File" fileName="${basedir}/log.txt" layout="${date} ${level}: &lt;${logger:shortName=true}&gt; ${message} ${exception:format=tostring}"/>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file"/>
</rules>
</nlog>
<appSettings>
<add key="rabbitmq.host" value="localhost"/>
<add key="rabbitmq.user" value="murano"/>
<add key="rabbitmq.password" value="murano"/>
<add key="rabbitmq.vhost" value="murano"/>
<add key="rabbitmq.resultExchange" value=""/>
<add key="rabbitmq.resultRoutingKey" value="-execution-results"/>
<add key="rabbitmq.durableMessages" value="true"/>
</appSettings>
</configuration>

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mirantis.Murano.WindowsAgent
{
class ExecutionPlan
{
public class Command
{
public string Name { get; set; }
public Dictionary<string, object> Arguments { get; set; }
}
public string[] Scripts { get; set; }
public LinkedList<Command> Commands { get; set; }
public int RebootOnCompletion { get; set; }
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mirantis.Murano.WindowsAgent
{
class MqMessage
{
private readonly Action ackFunc;
public MqMessage(Action ackFunc)
{
this.ackFunc = ackFunc;
}
public MqMessage()
{
}
public string Body { get; set; }
public string Id { get; set; }
public void Ack()
{
ackFunc();
}
}
}

View File

@ -0,0 +1,233 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using Newtonsoft.Json.Linq;
using NLog;
using Newtonsoft.Json;
namespace Mirantis.Murano.WindowsAgent
{
class PlanExecutor
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
class ExecutionResult
{
public bool IsException { get; set; }
public object Result { get; set; }
}
private readonly string path;
public PlanExecutor(string path)
{
this.path = path;
}
public bool RebootNeeded { get; set; }
public void Execute()
{
RebootNeeded = false;
var resultPath = this.path + ".result";
Runspace runSpace = null;
try
{
var plan = JsonConvert.DeserializeObject<ExecutionPlan>(File.ReadAllText(this.path));
List<ExecutionResult> currentResults = null;
try
{
currentResults = File.Exists(resultPath) ?
JsonConvert.DeserializeObject<List<ExecutionResult>>(File.ReadAllText(resultPath)) :
new List<ExecutionResult>();
}
catch(Exception exception)
{
Log.WarnException("Cannot deserialize previous execution result", exception);
currentResults = new List<ExecutionResult>();
}
runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
var runSpaceInvoker = new RunspaceInvoke(runSpace);
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
if (plan.Scripts != null)
{
var index = 0;
foreach (var script in plan.Scripts)
{
runSpaceInvoker.Invoke(Encoding.UTF8.GetString(Convert.FromBase64String(script)));
Log.Debug("Loaded script #{0}", ++index);
}
}
while (plan.Commands != null && plan.Commands.Any())
{
var command = plan.Commands.First();
Log.Debug("Preparing to execute command {0}", command.Name);
var pipeline = runSpace.CreatePipeline();
var psCommand = new Command(command.Name);
if (command.Arguments != null)
{
foreach (var kvp in command.Arguments)
{
var value = ConvertArgument(kvp.Value);
psCommand.Parameters.Add(kvp.Key, value);
}
}
Log.Info("Executing {0} {1}", command.Name, string.Join(" ",
(command.Arguments ?? new Dictionary<string, object>()).Select(
t => string.Format("{0}={1}", t.Key, t.Value == null ? "null" : t.Value.ToString()))));
pipeline.Commands.Add(psCommand);
try
{
var result = pipeline.Invoke();
Log.Debug("Command {0} executed", command.Name);
if (result != null)
{
currentResults.Add(new ExecutionResult {
IsException = false,
Result = result.Select(SerializePsObject).Where(obj => obj != null).ToList()
});
}
}
catch (Exception exception)
{
object additionInfo = null;
if (exception is ActionPreferenceStopException)
{
var apse = exception as ActionPreferenceStopException;
if (apse.ErrorRecord != null)
{
additionInfo = new {
ScriptStackTrace = apse.ErrorRecord.ScriptStackTrace,
PositionMessage = apse.ErrorRecord.InvocationInfo.PositionMessage
};
exception = apse.ErrorRecord.Exception;
}
}
Log.WarnException("Exception while executing command " + command.Name, exception);
currentResults.Add(new ExecutionResult
{
IsException = true,
Result = new[] {
exception.GetType().FullName, exception.Message, command.Name, additionInfo
}
});
break;
}
finally
{
plan.Commands.RemoveFirst();
File.WriteAllText(path, JsonConvert.SerializeObject(plan));
File.WriteAllText(resultPath, JsonConvert.SerializeObject(currentResults));
}
}
runSpace.Close();
var executionResult = JsonConvert.SerializeObject(new ExecutionResult {
IsException = false,
Result = currentResults
}, Formatting.Indented);
if (plan.RebootOnCompletion > 0)
{
if (plan.RebootOnCompletion == 1)
{
RebootNeeded = !currentResults.Any(t => t.IsException);
}
else
{
RebootNeeded = true;
}
}
File.WriteAllText(resultPath, executionResult);
}
catch (Exception exception)
{
Log.WarnException("Exception while processing execution plan", exception);
File.WriteAllText(resultPath, JsonConvert.SerializeObject(new ExecutionResult {
IsException = true,
Result = exception.Message
}, Formatting.Indented));
}
finally
{
if (runSpace != null)
{
try
{
runSpace.Close();
}
catch
{}
}
Log.Debug("Finished processing of execution plan");
}
}
private static object ConvertArgument(object arg)
{
if (arg is JArray)
{
var array = arg as JArray;
return array.Select(ConvertArgument).ToArray();
}
else if (arg is JValue)
{
var value = (JValue) arg;
return value.Value;
}
else if (arg is JObject)
{
var dict = (JObject)arg;
var result = new Hashtable();
foreach (var item in dict)
{
result.Add(item.Key, ConvertArgument(item.Value));
}
return result;
}
return arg;
}
private static object SerializePsObject(PSObject obj)
{
if (obj.BaseObject is PSCustomObject)
{
var result = new Dictionary<string, object>();
foreach (var property in obj.Properties.Where(p => p.IsGettable))
{
try
{
result[property.Name] = property.Value.ToString();
}
catch
{
}
}
return result;
}
else if (obj.BaseObject is IEnumerable<PSObject>)
{
return ((IEnumerable<PSObject>) obj.BaseObject).Select(SerializePsObject).ToArray();
}
else
{
return obj.BaseObject;
}
}
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Net;
using System.Text;
using System.Threading;
using NLog;
namespace Mirantis.Murano.WindowsAgent
{
[DisplayName("Murano Agent")]
sealed public class Program : WindowsService
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private volatile bool stop;
private Thread thread;
private RabbitMqClient rabbitMqClient;
private int delayFactor = 1;
private string plansDir;
static void Main(string[] args)
{
Start(new Program(), args);
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
Log.Info("Version 0.5.3");
this.rabbitMqClient = new RabbitMqClient();
var basePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
this.plansDir = Path.Combine(basePath, "plans");
if (!Directory.Exists(plansDir))
{
Directory.CreateDirectory(plansDir);
}
this.thread = new Thread(Loop);
this.thread.Start();
}
void Loop()
{
const string unknownName = "unknown";
while (!stop)
{
try
{
foreach (var file in Directory.GetFiles(this.plansDir, "*.json.result")
.Where(file => !File.Exists(Path.Combine(this.plansDir, Path.GetFileNameWithoutExtension(file)))))
{
var id = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file)) ?? unknownName;
if (id.Equals(unknownName, StringComparison.InvariantCultureIgnoreCase))
{
id = "";
}
var result = File.ReadAllText(file);
Log.Info("Sending results for {0}", id ?? unknownName);
rabbitMqClient.SendResult(new MqMessage { Body = result, Id = id });
File.Delete(file);
}
var path = Directory.EnumerateFiles(this.plansDir, "*.json").FirstOrDefault();
if (path == null)
{
var message = rabbitMqClient.GetMessage();
var id = message.Id;
if(string.IsNullOrEmpty(id))
{
id = unknownName;
}
path = Path.Combine(this.plansDir, string.Format("{0}.json", id));
File.WriteAllText(path, message.Body);
Log.Info("Received new execution plan {0}", id);
message.Ack();
}
else
{
var id = Path.GetFileNameWithoutExtension(path);
Log.Info("Executing exising plan {0}", id);
}
var executor = new PlanExecutor(path);
executor.Execute();
File.Delete(path);
delayFactor = 1;
if (stop) break;
if (executor.RebootNeeded)
{
Reboot();
}
}
catch (Exception exception)
{
WaitOnException(exception);
}
}
}
private void Reboot()
{
Log.Info("Going for reboot!!");
LogManager.Flush();
/*try
{
System.Diagnostics.Process.Start("shutdown.exe", "-r -t 0");
}
catch (Exception ex)
{
Log.ErrorException("Cannot execute shutdown.exe", ex);
}*/
try
{
PowerShell.Create().AddCommand("Restart-Computer").AddParameter("Force").Invoke();
}
catch (Exception exception)
{
Log.FatalException("Reboot exception", exception);
}
finally
{
Log.Info("Waiting for reboot");
for (var i = 0; i < 10 * 60 * 5 && !stop; i++)
{
Thread.Sleep(100);
}
Log.Info("Done waiting for reboot");
}
}
private void WaitOnException(Exception exception)
{
if (stop) return;
Log.WarnException("Exception in main loop", exception);
var i = 0;
while (!stop && i < 10 * (delayFactor * delayFactor))
{
Thread.Sleep(100);
i++;
}
delayFactor = Math.Min(delayFactor + 1, 6);
}
protected override void OnStop()
{
stop = true;
this.rabbitMqClient.Dispose();
base.OnStop();
}
}
}

View File

@ -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("WindowsAgent")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WindowsAgent")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[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("9591bf2c-f38b-47e0-a39d-ea9849356371")]
// 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")]

View File

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using NLog;
using RabbitMQ.Client;
namespace Mirantis.Murano.WindowsAgent
{
class RabbitMqClient : IDisposable
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static readonly ConnectionFactory connectionFactory;
private IConnection currentConnecton;
static RabbitMqClient()
{
var ssl = new SslOption {
Enabled = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.ssl"] ?? "false"),
Version = SslProtocols.Default,
AcceptablePolicyErrors = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.allowInvalidCA"] ?? "true") ?
SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None
};
var sslServerName = ConfigurationManager.AppSettings["rabbitmq.sslServerName"] ?? "";
ssl.ServerName = sslServerName;
if (String.IsNullOrWhiteSpace(sslServerName))
{
ssl.AcceptablePolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch;
}
connectionFactory = new ConnectionFactory {
HostName = ConfigurationManager.AppSettings["rabbitmq.host"] ?? "localhost",
UserName = ConfigurationManager.AppSettings["rabbitmq.user"] ?? "guest",
Password = ConfigurationManager.AppSettings["rabbitmq.password"] ??"guest",
Protocol = Protocols.DefaultProtocol,
VirtualHost = ConfigurationManager.AppSettings["rabbitmq.vhost"] ?? "/",
Port = int.Parse(ConfigurationManager.AppSettings["rabbitmq.port"] ?? "5672"),
RequestedHeartbeat = 10,
Ssl = ssl
};
}
public RabbitMqClient()
{
}
public MqMessage GetMessage()
{
var queueName = ConfigurationManager.AppSettings["rabbitmq.inputQueue"] ?? Dns.GetHostName().ToLower();
try
{
IConnection connection = null;
lock (this)
{
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
}
var session = connection.CreateModel();
session.BasicQos(0, 1, false);
//session.QueueDeclare(queueName, true, false, false, null);
var consumer = new QueueingBasicConsumer(session);
var consumeTag = session.BasicConsume(queueName, false, consumer);
var e = (RabbitMQ.Client.Events.BasicDeliverEventArgs) consumer.Queue.Dequeue();
Action ackFunc = delegate {
session.BasicAck(e.DeliveryTag, false);
session.BasicCancel(consumeTag);
session.Close();
};
return new MqMessage(ackFunc) {
Body = Encoding.UTF8.GetString(e.Body),
Id = e.BasicProperties.MessageId
};
}
catch (Exception exception)
{
Dispose();
throw;
}
}
public void SendResult(MqMessage message)
{
var exchangeName = ConfigurationManager.AppSettings["rabbitmq.resultExchange"] ?? "";
var resultRoutingKey = ConfigurationManager.AppSettings["rabbitmq.resultRoutingKey"] ?? "-execution-results";
bool durable = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.durableMessages"] ?? "true");
try
{
IConnection connection = null;
lock (this)
{
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
}
var session = connection.CreateModel();
/*if (!string.IsNullOrEmpty(resultQueue))
{
//session.QueueDeclare(resultQueue, true, false, false, null);
if (!string.IsNullOrEmpty(exchangeName))
{
session.ExchangeBind(exchangeName, resultQueue, resultQueue);
}
}*/
var basicProperties = session.CreateBasicProperties();
basicProperties.SetPersistent(durable);
basicProperties.MessageId = message.Id;
basicProperties.ContentType = "application/json";
session.BasicPublish(exchangeName, resultRoutingKey, basicProperties, Encoding.UTF8.GetBytes(message.Body));
session.Close();
}
catch (Exception)
{
Dispose();
throw;
}
}
public void Dispose()
{
lock (this)
{
try
{
if (this.currentConnecton != null)
{
this.currentConnecton.Close();
}
}
catch
{
}
finally
{
this.currentConnecton = null;
}
}
}
}
}

View File

@ -0,0 +1,37 @@
{
"Scripts":
[
"ZnVuY3Rpb24gdDMgeyAxMjsgcmV0dXJuICJ0ZXN0IiB9",
"ZnVuY3Rpb24gTmV3LVBlcnNvbigpDQp7DQogIHBhcmFtICgkRmlyc3ROYW1lLCAkTGFzdE5hbWUsICRQaG9uZSkNCg0KICAkcGVyc29uID0gbmV3LW9iamVjdCBQU09iamVjdA0KDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBGaXJzdCAtVmFsdWUgJEZpcnN0TmFtZQ0KICAkcGVyc29uIHwgYWRkLW1lbWJlciAtdHlwZSBOb3RlUHJvcGVydHkgLU5hbWUgTGFzdCAtVmFsdWUgJExhc3ROYW1lDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBQaG9uZSAtVmFsdWUgJFBob25lDQoNCiAgcmV0dXJuICRwZXJzb24NCn0=",
"ZnVuY3Rpb24gVGVzdFRocm93KCkNCnsNCglUaHJvdyBbc3lzdGVtLkluZGV4T3V0T2ZSYW5nZUV4Y2VwdGlvbl0gDQp9"
],
"Commands" :
[
{
"Name": "New-Person",
"Arguments" :
{
"FirstName": "MyFirstName",
"LastName": "MyLastName",
"Phone": "123-456"
}
},
{
"Name": "t3",
"Arguments" :
{
}
},
{
"Name": "Get-Date",
},
{
"Name": "TestThrow",
}
],
"RebootOnCompletion": 0
}

View File

@ -0,0 +1,111 @@
using System;
using System.Configuration.Install;
using System.Reflection;
using System.ServiceProcess;
using NLog;
namespace Mirantis.Murano.WindowsAgent
{
public class ServiceManager
{
private readonly string serviceName;
public ServiceManager(string serviceName)
{
this.serviceName = serviceName;
}
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public bool Restart(string[] args, TimeSpan timeout)
{
var service = new ServiceController(serviceName);
try
{
var millisec1 = TimeSpan.FromMilliseconds(Environment.TickCount);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
Log.Info("Service is stopped");
// count the rest of the timeout
var millisec2 = TimeSpan.FromMilliseconds(Environment.TickCount);
timeout = timeout - (millisec2 - millisec1);
service.Start(args);
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
Log.Info("Service has started");
return true;
}
catch (Exception ex)
{
Log.ErrorException("Cannot restart service " + serviceName, ex);
return false;
}
}
public bool Stop(TimeSpan timeout)
{
var service = new ServiceController(serviceName);
try
{
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
return true;
}
catch (Exception ex)
{
Log.ErrorException("Cannot stop service " + serviceName, ex);
return false;
}
}
public bool Start(string[] args, TimeSpan timeout)
{
var service = new ServiceController(serviceName);
try
{
service.Start(args);
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
return true;
}
catch (Exception ex)
{
Log.ErrorException("Cannot start service " + serviceName, ex);
return false;
}
}
public bool Install()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { Assembly.GetEntryAssembly().Location });
}
catch(Exception ex)
{
Log.ErrorException("Cannot install service " + serviceName, ex);
return false;
}
return true;
}
public bool Uninstall()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { "/u", Assembly.GetEntryAssembly().Location });
}
catch (Exception ex)
{
Log.ErrorException("Cannot uninstall service " + serviceName, ex);
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.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>{F7E2A8D5-6D24-4651-A4BC-1024D59F4903}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Mirantis.Murano.WindowsAgent</RootNamespace>
<AssemblyName>WindowsAgent</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</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>
<Prefer32Bit>false</Prefer32Bit>
</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>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="RabbitMQ.Client">
<HintPath>..\packages\RabbitMQ.Client.3.0.2\lib\net30\RabbitMQ.Client.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll</HintPath>
</Reference>
<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.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ExecutionPlan.cs" />
<Compile Include="MqMessage.cs" />
<Compile Include="PlanExecutor.cs" />
<Compile Include="Program.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RabbitMqClient.cs" />
<Compile Include="ServiceManager.cs" />
<Compile Include="WindowsService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="WindowsServiceInstaller.cs">
<SubType>Component</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="SampleExecutionPlan.json" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>$(SolutionDir)Tools\nuget install $(ProjectDir)packages.config -o $(SolutionDir)Packages</PreBuildEvent>
</PropertyGroup>
<!-- 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>

View File

@ -0,0 +1,95 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using NLog;
namespace Mirantis.Murano.WindowsAgent
{
public abstract class WindowsService : ServiceBase
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public bool RunningAsService { get; private set; }
protected static void Start(WindowsService service, string[] arguments)
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
if (arguments.Contains("/install", StringComparer.OrdinalIgnoreCase))
{
new ServiceManager(service.ServiceName).Install();
}
else if (arguments.Contains("/uninstall", StringComparer.OrdinalIgnoreCase))
{
new ServiceManager(service.ServiceName).Uninstall();
}
else if (arguments.Contains("/start", StringComparer.OrdinalIgnoreCase))
{
new ServiceManager(service.ServiceName).Start(Environment.GetCommandLineArgs(), TimeSpan.FromMinutes(1));
}
else if (arguments.Contains("/stop", StringComparer.OrdinalIgnoreCase))
{
new ServiceManager(service.ServiceName).Stop(TimeSpan.FromMinutes(1));
}
else if (arguments.Contains("/restart", StringComparer.OrdinalIgnoreCase))
{
new ServiceManager(service.ServiceName).Restart(Environment.GetCommandLineArgs(), TimeSpan.FromMinutes(1));
}
else if (!arguments.Contains("/console", StringComparer.OrdinalIgnoreCase))
{
service.RunningAsService = true;
Run(service);
}
else
{
try
{
service.RunningAsService = false;
Console.Title = service.ServiceName;
service.OnStart(Environment.GetCommandLineArgs());
service.WaitForExitSignal();
}
finally
{
service.OnStop();
service.Dispose();
}
}
}
protected WindowsService()
{
var displayNameAttribute =
this.GetType().GetCustomAttributes(typeof (DisplayNameAttribute), false).Cast<DisplayNameAttribute>().
FirstOrDefault();
if(displayNameAttribute != null)
{
ServiceName = displayNameAttribute.DisplayName;
}
}
protected virtual void WaitForExitSignal()
{
Console.WriteLine("Press ESC to exit");
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
}
}
protected override void OnStart(string[] args)
{
Log.Info("Service {0} started", ServiceName);
base.OnStart(args);
}
protected override void OnStop()
{
Log.Info("Service {0} exited", ServiceName);
base.OnStop();
}
}
}

View File

@ -0,0 +1,39 @@
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
namespace Mirantis.Murano.WindowsAgent
{
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
public WindowsServiceInstaller()
{
var processInstaller = new ServiceProcessInstaller { Account = ServiceAccount.LocalSystem };
foreach (var type in Assembly.GetEntryAssembly().GetExportedTypes().Where(t => t.IsSubclassOf(typeof(ServiceBase))))
{
var nameAttribute = type.GetCustomAttributes(typeof (DisplayNameAttribute), false)
.Cast<DisplayNameAttribute>().FirstOrDefault();
if(nameAttribute == null) continue;
var serviceInstaller = new ServiceInstaller {
StartType = ServiceStartMode.Automatic,
ServiceName = nameAttribute.DisplayName,
DisplayName = nameAttribute.DisplayName
};
var descriptionAttribute = type.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>().FirstOrDefault();
if(descriptionAttribute != null)
{
serviceInstaller.Description = descriptionAttribute.Description;
}
Installers.Add(serviceInstaller);
}
Installers.Add(processInstaller);
}
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
<package id="NLog" version="2.0.0.2000" targetFramework="net45" />
<package id="RabbitMQ.Client" version="3.0.2" targetFramework="net45" />
</packages>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\WindowsAgent\packages.config" />
</repositories>