[KEERO-83] Windows Agent initial implementation
This commit is contained in:
parent
1273092b6a
commit
d141fbc4dc
BIN
WindowsAgent/Tools/NuGet.exe
Normal file
BIN
WindowsAgent/Tools/NuGet.exe
Normal file
Binary file not shown.
20
WindowsAgent/WindowsAgent.sln
Normal file
20
WindowsAgent/WindowsAgent.sln
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
21
WindowsAgent/WindowsAgent/App.config
Normal file
21
WindowsAgent/WindowsAgent/App.config
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
|
||||||
|
</configSections>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
</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}: <${logger:shortName=true}> ${message} ${exception:format=tostring}"/>
|
||||||
|
|
||||||
|
</targets>
|
||||||
|
|
||||||
|
<rules>
|
||||||
|
<logger name="*" minlevel="Debug" writeTo="file" />
|
||||||
|
</rules>
|
||||||
|
</nlog>
|
||||||
|
</configuration>
|
20
WindowsAgent/WindowsAgent/ExecutionPlan.cs
Normal file
20
WindowsAgent/WindowsAgent/ExecutionPlan.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.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; }
|
||||||
|
}
|
||||||
|
}
|
25
WindowsAgent/WindowsAgent/MqMessage.cs
Normal file
25
WindowsAgent/WindowsAgent/MqMessage.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.WindowsAgent
|
||||||
|
{
|
||||||
|
class MqMessage
|
||||||
|
{
|
||||||
|
private readonly Action ackFunc;
|
||||||
|
|
||||||
|
public MqMessage(Action ackFunc)
|
||||||
|
{
|
||||||
|
this.ackFunc = ackFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
public void Ack()
|
||||||
|
{
|
||||||
|
ackFunc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
WindowsAgent/WindowsAgent/PlanExecutor.cs
Normal file
139
WindowsAgent/WindowsAgent/PlanExecutor.cs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
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;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.WindowsAgent
|
||||||
|
{
|
||||||
|
class PlanExecutor
|
||||||
|
{
|
||||||
|
class ExecutionResult
|
||||||
|
{
|
||||||
|
public bool IsException { get; set; }
|
||||||
|
public object Result { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string path;
|
||||||
|
|
||||||
|
public PlanExecutor(string path)
|
||||||
|
{
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Execute()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var plan = JsonConvert.DeserializeObject<ExecutionPlan>(File.ReadAllText(this.path));
|
||||||
|
var resultPath = this.path + ".result";
|
||||||
|
List<object> currentResults = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentResults = JsonConvert.DeserializeObject<List<object>>(File.ReadAllText(resultPath));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
currentResults = new List<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var runSpace = RunspaceFactory.CreateRunspace();
|
||||||
|
runSpace.Open();
|
||||||
|
|
||||||
|
var runSpaceInvoker = new RunspaceInvoke(runSpace);
|
||||||
|
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
|
||||||
|
if (plan.Scripts != null)
|
||||||
|
{
|
||||||
|
foreach (var script in plan.Scripts)
|
||||||
|
{
|
||||||
|
runSpaceInvoker.Invoke(Encoding.UTF8.GetString(Convert.FromBase64String(script)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (plan.Commands != null && plan.Commands.Any())
|
||||||
|
{
|
||||||
|
var command = plan.Commands.First();
|
||||||
|
|
||||||
|
var pipeline = runSpace.CreatePipeline();
|
||||||
|
var psCommand = new Command(command.Name);
|
||||||
|
if (command.Arguments != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in command.Arguments)
|
||||||
|
{
|
||||||
|
psCommand.Parameters.Add(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pipeline.Commands.Add(psCommand);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = pipeline.Invoke();
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
currentResults.Add(new ExecutionResult {
|
||||||
|
IsException = false,
|
||||||
|
Result = result.Select(SerializePsObject).Where(obj => obj != null).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
currentResults.Add(new ExecutionResult {
|
||||||
|
IsException = true,
|
||||||
|
Result = new[] {
|
||||||
|
exception.GetType().FullName, exception.Message
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
File.Delete(resultPath);
|
||||||
|
return executionResult;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new ExecutionResult {
|
||||||
|
IsException = true,
|
||||||
|
Result = ex.Message
|
||||||
|
}, Formatting.Indented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return obj.BaseObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
78
WindowsAgent/WindowsAgent/Program.cs
Normal file
78
WindowsAgent/WindowsAgent/Program.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.WindowsAgent
|
||||||
|
{
|
||||||
|
[DisplayName("Keero Agent")]
|
||||||
|
sealed public class Program : WindowsService
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
private volatile bool stop;
|
||||||
|
private Thread thread;
|
||||||
|
private RabbitMqClient rabbitMqClient;
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Start(new Program(), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStart(string[] args)
|
||||||
|
{
|
||||||
|
base.OnStart(args);
|
||||||
|
this.rabbitMqClient = new RabbitMqClient();
|
||||||
|
this.thread = new Thread(Loop);
|
||||||
|
this.thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loop()
|
||||||
|
{
|
||||||
|
const string filePath = "data.json";
|
||||||
|
while (!stop)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
var message = rabbitMqClient.GetMessage();
|
||||||
|
File.WriteAllText(filePath, message.Body);
|
||||||
|
message.Ack();
|
||||||
|
}
|
||||||
|
var result = new PlanExecutor(filePath).Execute();
|
||||||
|
if(stop) break;
|
||||||
|
rabbitMqClient.SendResult(result);
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
WaitOnException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WaitOnException(Exception exception)
|
||||||
|
{
|
||||||
|
if (stop) return;
|
||||||
|
Log.WarnException("Exception in main loop", exception);
|
||||||
|
var i = 0;
|
||||||
|
while (!stop && i < 10)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStop()
|
||||||
|
{
|
||||||
|
stop = true;
|
||||||
|
this.rabbitMqClient.Dispose();
|
||||||
|
Console.WriteLine("Stop");
|
||||||
|
base.OnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
36
WindowsAgent/WindowsAgent/Properties/AssemblyInfo.cs
Normal file
36
WindowsAgent/WindowsAgent/Properties/AssemblyInfo.cs
Normal 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")]
|
124
WindowsAgent/WindowsAgent/RabbitMqClient.cs
Normal file
124
WindowsAgent/WindowsAgent/RabbitMqClient.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.WindowsAgent
|
||||||
|
{
|
||||||
|
class RabbitMqClient : IDisposable
|
||||||
|
{
|
||||||
|
private static readonly ConnectionFactory connectionFactory;
|
||||||
|
private IConnection currentConnecton;
|
||||||
|
|
||||||
|
static RabbitMqClient()
|
||||||
|
{
|
||||||
|
connectionFactory = new ConnectionFactory {
|
||||||
|
HostName = ConfigurationManager.AppSettings["rabbitmq.host"] ?? "localhost",
|
||||||
|
UserName = ConfigurationManager.AppSettings["rabbitmq.user"] ?? "guest",
|
||||||
|
Password = ConfigurationManager.AppSettings["rabbitmq.password"] ??"guest",
|
||||||
|
Protocol = Protocols.FromEnvironment(),
|
||||||
|
VirtualHost = ConfigurationManager.AppSettings["rabbitmq.vhost"] ?? "/",
|
||||||
|
RequestedHeartbeat = 10
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public RabbitMqClient()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqMessage GetMessage()
|
||||||
|
{
|
||||||
|
var queueName = ConfigurationManager.AppSettings["rabbitmq.inputQueue"] ?? Environment.MachineName.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);
|
||||||
|
Console.WriteLine("Deq");
|
||||||
|
var e = (RabbitMQ.Client.Events.BasicDeliverEventArgs)consumer.Queue.Dequeue();
|
||||||
|
Console.WriteLine("Message received");
|
||||||
|
Action ackFunc = delegate {
|
||||||
|
session.BasicAck(e.DeliveryTag, false);
|
||||||
|
session.BasicCancel(consumeTag);
|
||||||
|
session.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return new MqMessage(ackFunc) {
|
||||||
|
Body = Encoding.UTF8.GetString(e.Body)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendResult(string text)
|
||||||
|
{
|
||||||
|
var exchangeName = ConfigurationManager.AppSettings["rabbitmq.resultExchange"] ?? "";
|
||||||
|
var resultQueue = ConfigurationManager.AppSettings["rabbitmq.resultQueue"] ?? "-execution-results";
|
||||||
|
|
||||||
|
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(true);
|
||||||
|
basicProperties.ContentType = "application/json";
|
||||||
|
session.BasicPublish(exchangeName, resultQueue, basicProperties, Encoding.UTF8.GetBytes(text));
|
||||||
|
session.Close();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (this.currentConnecton != null)
|
||||||
|
{
|
||||||
|
this.currentConnecton.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.currentConnecton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
WindowsAgent/WindowsAgent/SampleExecutionPlan.json
Normal file
36
WindowsAgent/WindowsAgent/SampleExecutionPlan.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"Scripts":
|
||||||
|
[
|
||||||
|
"ZnVuY3Rpb24gdDMgeyAxMjsgcmV0dXJuICJ0ZXN0IiB9",
|
||||||
|
"ZnVuY3Rpb24gTmV3LVBlcnNvbigpDQp7DQogIHBhcmFtICgkRmlyc3ROYW1lLCAkTGFzdE5hbWUsICRQaG9uZSkNCg0KICAkcGVyc29uID0gbmV3LW9iamVjdCBQU09iamVjdA0KDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBGaXJzdCAtVmFsdWUgJEZpcnN0TmFtZQ0KICAkcGVyc29uIHwgYWRkLW1lbWJlciAtdHlwZSBOb3RlUHJvcGVydHkgLU5hbWUgTGFzdCAtVmFsdWUgJExhc3ROYW1lDQogICRwZXJzb24gfCBhZGQtbWVtYmVyIC10eXBlIE5vdGVQcm9wZXJ0eSAtTmFtZSBQaG9uZSAtVmFsdWUgJFBob25lDQoNCiAgcmV0dXJuICRwZXJzb24NCn0=",
|
||||||
|
"ZnVuY3Rpb24gVGVzdFRocm93KCkNCnsNCglUaHJvdyBbc3lzdGVtLkluZGV4T3V0T2ZSYW5nZUV4Y2VwdGlvbl0gDQp9"
|
||||||
|
],
|
||||||
|
"Commands" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Name": "New-Person",
|
||||||
|
"Arguments" :
|
||||||
|
{
|
||||||
|
"FirstName": "MyFirstName",
|
||||||
|
"LastName": "MyLastName",
|
||||||
|
"Phone": "123-456"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "t3",
|
||||||
|
"Arguments" :
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Get-Date",
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "TestThrow",
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
111
WindowsAgent/WindowsAgent/ServiceManager.cs
Normal file
111
WindowsAgent/WindowsAgent/ServiceManager.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
92
WindowsAgent/WindowsAgent/WindowsAgent.csproj
Normal file
92
WindowsAgent/WindowsAgent/WindowsAgent.csproj
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?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.Keero.WindowsAgent</RootNamespace>
|
||||||
|
<AssemblyName>WindowsAgent</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="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>
|
95
WindowsAgent/WindowsAgent/WindowsService.cs
Normal file
95
WindowsAgent/WindowsAgent/WindowsService.cs
Normal 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.Keero.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
WindowsAgent/WindowsAgent/WindowsServiceInstaller.cs
Normal file
39
WindowsAgent/WindowsAgent/WindowsServiceInstaller.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
|
||||||
|
namespace Mirantis.Keero.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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
WindowsAgent/WindowsAgent/packages.config
Normal file
6
WindowsAgent/WindowsAgent/packages.config
Normal 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>
|
4
WindowsAgent/packages/repositories.config
Normal file
4
WindowsAgent/packages/repositories.config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<repositories>
|
||||||
|
<repository path="..\WindowsAgent\packages.config" />
|
||||||
|
</repositories>
|
Loading…
x
Reference in New Issue
Block a user