Skip to content

Commit d674e80

Browse files
included utilization script and updated readme
1 parent 644b404 commit d674e80

9 files changed

+510
-2
lines changed

AutomationScript.sln

+16
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actions", "Actions", "{F596
4040
EndProject
4141
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking Info_1", "Booking Info_1\Booking Info_1.csproj", "{C045EE64-A606-49E5-8270-E8F6183478DD}"
4242
EndProject
43+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Show Resource Utilization", "Show Resource Utilization", "{EC4FDFAF-4D3F-4915-8F93-FB8AFBB4DBC5}"
44+
ProjectSection(SolutionItems) = preProject
45+
Show Resource Utilization.xml = Show Resource Utilization.xml
46+
EndProjectSection
47+
EndProject
48+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actions", "Actions", "{42819BA1-E2AA-43B7-90FB-E3C39588E1F7}"
49+
EndProject
50+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Show Resource Utilization_1", "Show Resource Utilization_1\Show Resource Utilization_1.csproj", "{AC09FE56-65EC-46E4-B321-19D5118BD3A4}"
51+
EndProject
4352
Global
4453
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4554
Debug|Any CPU = Debug|Any CPU
@@ -50,6 +59,10 @@ Global
5059
{C045EE64-A606-49E5-8270-E8F6183478DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
5160
{C045EE64-A606-49E5-8270-E8F6183478DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
5261
{C045EE64-A606-49E5-8270-E8F6183478DD}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{AC09FE56-65EC-46E4-B321-19D5118BD3A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{AC09FE56-65EC-46E4-B321-19D5118BD3A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{AC09FE56-65EC-46E4-B321-19D5118BD3A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{AC09FE56-65EC-46E4-B321-19D5118BD3A4}.Release|Any CPU.Build.0 = Release|Any CPU
5366
EndGlobalSection
5467
GlobalSection(SolutionProperties) = preSolution
5568
HideSolutionNode = FALSE
@@ -60,6 +73,9 @@ Global
6073
{813AB95D-61BC-4F01-86C5-25DC08EE1085} = {BD102EF9-072D-4100-ACA0-49F40EF73886}
6174
{F5965FC7-2A7E-4371-B3CB-A1079B59FFFE} = {813AB95D-61BC-4F01-86C5-25DC08EE1085}
6275
{C045EE64-A606-49E5-8270-E8F6183478DD} = {F5965FC7-2A7E-4371-B3CB-A1079B59FFFE}
76+
{EC4FDFAF-4D3F-4915-8F93-FB8AFBB4DBC5} = {BD102EF9-072D-4100-ACA0-49F40EF73886}
77+
{42819BA1-E2AA-43B7-90FB-E3C39588E1F7} = {EC4FDFAF-4D3F-4915-8F93-FB8AFBB4DBC5}
78+
{AC09FE56-65EC-46E4-B321-19D5118BD3A4} = {42819BA1-E2AA-43B7-90FB-E3C39588E1F7}
6379
EndGlobalSection
6480
GlobalSection(ExtensibilityGlobals) = postSolution
6581
SolutionGuid = {07BEF66B-4BEF-40DC-8C79-797CD8E3FD60}

Documentation/ResourceUtilization.png

15.6 KB
Loading

README.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ This repository contains an automation script solution with scripts that can be
44

55
The following scrips are currently available:
66

7-
[Booking Info](#booking-info)
7+
- [Booking Info](#booking-info)
8+
- [Show Resource Utilization](#show-resource-utilization)
89

910
## Pre-requisites
1011

@@ -14,4 +15,10 @@ Kindly ensure that your DataMiner system and your Microsoft Teams adhere to the
1415

1516
Automation script that returns the ongoing bookings from the connected DataMiner system. More specifically the return value will contain the total amount of ongoing bookings including a table which will show by default the ongoing bookings whose end is nearest (default filter maximum 10 bookings).
1617

17-
![Booking Info example](/Documentation/OngoingBookingsChatOpsCommand.png)
18+
![Booking Info example](/Documentation/OngoingBookingsChatOpsCommand.png)
19+
20+
## Show Resource Utilization
21+
22+
Automation script that returns the utilization (%) of each resource in the specified resource pool for the last week.
23+
24+
![Resource Utilization](/Documentation/ResourceUtilization.png)

Show Resource Utilization.xml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<DMSScript options="272" xmlns="http://www.skyline.be/automation">
3+
<Name>Show Resource Utilization</Name>
4+
<Description></Description>
5+
<Type>Automation</Type>
6+
<Author>SKYLINE2\Joey</Author>
7+
<CheckSets>FALSE</CheckSets>
8+
<Folder>bot</Folder>
9+
10+
<Protocols>
11+
</Protocols>
12+
13+
<Memory>
14+
</Memory>
15+
16+
<Parameters>
17+
<ScriptParameter id="2" type="string" values="">
18+
<Description>Resource Pool Name</Description>
19+
</ScriptParameter>
20+
</Parameters>
21+
22+
<Script>
23+
<Exe id="1" type="csharp">
24+
<Value><![CDATA[[Project:Show Resource Utilization_1]]]></Value>
25+
<!--<Param type="debug">true</Param>-->
26+
<Message></Message>
27+
</Exe>
28+
</Script>
29+
</DMSScript>
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
indent_style = tab
5+
indent_size = 4
6+
tab_width = 4
7+
end_of_line = crlf
8+
trim_trailing_whitespace = true
9+
10+
[*.cs]
11+
dotnet_sort_system_directives_first = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
[assembly: AssemblyTitle("Show Resource Utilization_1")]
6+
[assembly: AssemblyDescription("")]
7+
[assembly: AssemblyConfiguration("")]
8+
[assembly: AssemblyCompany("Skyline Communications")]
9+
[assembly: AssemblyProduct("Show Resource Utilization_1")]
10+
[assembly: AssemblyCopyright("Copyright © Skyline Communications")]
11+
[assembly: AssemblyTrademark("")]
12+
[assembly: AssemblyCulture("")]
13+
14+
[assembly: ComVisible(false)]
15+
[assembly: Guid("B714E9DC-B0E6-46E8-950B-47806444F71D")]
16+
[assembly: AssemblyVersion("1.0.0.0")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
****************************************************************************
3+
* Copyright (c) 2023, Skyline Communications NV All Rights Reserved. *
4+
****************************************************************************
5+
6+
By using this script, you expressly agree with the usage terms and
7+
conditions set out below.
8+
This script and all related materials are protected by copyrights and
9+
other intellectual property rights that exclusively belong
10+
to Skyline Communications.
11+
12+
A user license granted for this script is strictly for personal use only.
13+
This script may not be used in any way by anyone without the prior
14+
written consent of Skyline Communications. Any sublicensing of this
15+
script is forbidden.
16+
17+
Any modifications to this script by the user are only allowed for
18+
personal use and within the intended purpose of the script,
19+
and will remain the sole responsibility of the user.
20+
Skyline Communications will not be responsible for any damages or
21+
malfunctions whatsoever of the script resulting from a modification
22+
or adaptation by the user.
23+
24+
The content of this script is confidential information.
25+
The user hereby agrees to keep this confidential information strictly
26+
secret and confidential and not to disclose or reveal it, in whole
27+
or in part, directly or indirectly to any person, entity, organization
28+
or administration without the prior written consent of
29+
Skyline Communications.
30+
31+
Any inquiries can be addressed to:
32+
33+
Skyline Communications NV
34+
Ambachtenstraat 33
35+
B-8870 Izegem
36+
Belgium
37+
Tel. : +32 51 31 35 69
38+
Fax. : +32 51 31 01 29
39+
E-mail : info@skyline.be
40+
Web : www.skyline.be
41+
Contact : Ben Vandenberghe
42+
43+
****************************************************************************
44+
Revision History:
45+
46+
DATE VERSION AUTHOR COMMENTS
47+
48+
dd/mm/2023 1.0.0.1 XXX, Skyline Initial version
49+
****************************************************************************
50+
*/
51+
52+
namespace Show_Resource_Utilization_1
53+
{
54+
using System;
55+
using System.Collections.Generic;
56+
using System.Linq;
57+
using AdaptiveCards;
58+
using Newtonsoft.Json;
59+
using Skyline.DataMiner.Automation;
60+
using Skyline.DataMiner.Net.Helper;
61+
using Skyline.DataMiner.Net.Messages;
62+
using Skyline.DataMiner.Net.Messages.SLDataGateway;
63+
using Skyline.DataMiner.Net.ResourceManager.Objects;
64+
using Skyline.DataMiner.Net.Time;
65+
66+
/// <summary>
67+
/// DataMiner Script Class.
68+
/// </summary>
69+
public class Script
70+
{
71+
private static int durationInDays = 7;
72+
DateTime start;
73+
DateTime end;
74+
75+
/// <summary>
76+
/// The Script entry point.
77+
/// </summary>
78+
/// <param name="engine">Link with SLAutomation process.</param>
79+
public void Run(Engine engine)
80+
{
81+
try
82+
{
83+
RunSafe(engine);
84+
}
85+
catch (Exception ex)
86+
{
87+
ReturnErrorMessage(engine, ex.Message);
88+
}
89+
}
90+
91+
private void RunSafe(Engine engine)
92+
{
93+
end = DateTime.UtcNow;
94+
start = end.AddDays(-durationInDays);
95+
96+
var resourcePoolName = engine.GetScriptParam("Resource Pool Name").Value;
97+
98+
ResourceManagerHelper rmHelper = new ResourceManagerHelper();
99+
rmHelper.RequestResponseEvent += (sender, e) => e.responseMessage = engine.SendSLNetSingleResponseMessage(e.requestMessage);
100+
101+
List<Resource> resources = GetResourcesByPoolName(rmHelper, resourcePoolName);
102+
if (resources.Count == 0)
103+
{
104+
ReturnErrorMessage(engine, "No resources available.");
105+
106+
return;
107+
}
108+
109+
var resourceMapping = resources.ToDictionary(r => r.ID, r => new ResourceInfo { Resource = r, Duration = 0 });
110+
foreach (var reservation in GetReservationsByResources(rmHelper, resourceMapping.Keys.ToList()))
111+
{
112+
reservation.ResourcesInReservationInstance
113+
.Where(resource => resourceMapping.Keys.Contains(resource.GUID))
114+
.ForEach(resource => UpdateTimeRange(resource.GUID, reservation.TimeRange, resourceMapping));
115+
}
116+
117+
ReturnResultMessage(engine, resourceMapping.Values.ToList(), false);
118+
}
119+
120+
private List<Resource> GetResourcesByPoolName(ResourceManagerHelper rmHelper, string name)
121+
{
122+
ResourcePool resourcePool = rmHelper.GetResourcePools(new ResourcePool { Name = name }).FirstOrDefault();
123+
if (resourcePool == null)
124+
{
125+
throw new ArgumentException($"No resource pool found with name '{name}'.");
126+
}
127+
128+
return rmHelper.GetResources(ResourceExposers.PoolGUIDs.Contains(resourcePool.GUID)).ToList();
129+
}
130+
131+
private List<ReservationInstance> GetReservationsByResources(ResourceManagerHelper rmHelper, List<Guid> resourceIds)
132+
{
133+
var resourceFilter = new ORFilterElement<ReservationInstance>(resourceIds.Select(id => ReservationInstanceExposers.ResourceIDsInReservationInstance.Contains(id)).ToArray());
134+
135+
var now = DateTime.UtcNow;
136+
var timeRangeUtc = new TimeRangeUtc(now.AddDays(-7), now);
137+
138+
var filter = resourceFilter
139+
.AND(ReservationInstanceExposers.Start.LessThanOrEqual(timeRangeUtc.Stop)
140+
.AND(ReservationInstanceExposers.End.GreaterThanOrEqual(timeRangeUtc.Start)));
141+
142+
return rmHelper.GetReservationInstances(filter).ToList();
143+
}
144+
145+
private void UpdateTimeRange(Guid resourceId, TimeRangeUtc timeRange, Dictionary<Guid, ResourceInfo> dic)
146+
{
147+
DateTime startToUse = (timeRange.Start < start) ? start : timeRange.Start;
148+
DateTime endToUse = (timeRange.Stop > end) ? end : timeRange.Stop;
149+
150+
dic[resourceId].Duration += (endToUse - startToUse).TotalMinutes;
151+
}
152+
153+
private void ReturnResultMessage(Engine engine, List<ResourceInfo> resources, bool skipNotUsed)
154+
{
155+
var table = new AdaptiveTable
156+
{
157+
Type = "Table",
158+
FirstRowAsHeaders = true,
159+
Columns = new List<AdaptiveTableColumnDefinition>
160+
{
161+
new AdaptiveTableColumnDefinition
162+
{
163+
Width = 250,
164+
},
165+
new AdaptiveTableColumnDefinition
166+
{
167+
Width = 100,
168+
},
169+
},
170+
Rows = new List<AdaptiveTableRow>
171+
{
172+
new AdaptiveTableRow
173+
{
174+
Type = "TableRow",
175+
Cells = new List<AdaptiveTableCell>
176+
{
177+
new AdaptiveTableCell
178+
{
179+
Type = "TableCell",
180+
Items = new List<AdaptiveElement>
181+
{
182+
new AdaptiveTextBlock("Resource")
183+
{
184+
Type = "TextBlock",
185+
Weight = AdaptiveTextWeight.Bolder,
186+
},
187+
},
188+
},
189+
new AdaptiveTableCell
190+
{
191+
Type = "TableCell",
192+
Items = new List<AdaptiveElement>
193+
{
194+
new AdaptiveTextBlock("Utilization")
195+
{
196+
Type = "TextBlock",
197+
Weight = AdaptiveTextWeight.Bolder,
198+
},
199+
},
200+
},
201+
},
202+
},
203+
},
204+
};
205+
206+
Double maxDuration = 24 * 60 * 7;
207+
208+
foreach (var resourceInfo in resources)
209+
{
210+
if (skipNotUsed && resourceInfo.Duration.Equals(0))
211+
{
212+
continue;
213+
}
214+
215+
var row = new AdaptiveTableRow
216+
{
217+
Type = "TableRow",
218+
Cells = new List<AdaptiveTableCell>
219+
{
220+
new AdaptiveTableCell
221+
{
222+
Type = "TableCell",
223+
Items = new List<AdaptiveElement>
224+
{
225+
new AdaptiveTextBlock(resourceInfo.Resource.Name)
226+
{
227+
Type = "TextBlock",
228+
},
229+
},
230+
},
231+
new AdaptiveTableCell
232+
{
233+
Type = "TableCell",
234+
Items = new List<AdaptiveElement>
235+
{
236+
new AdaptiveTextBlock($"{resourceInfo.Duration / maxDuration * 100:N2}%")
237+
{
238+
Type = "TextBlock",
239+
},
240+
},
241+
},
242+
},
243+
};
244+
245+
table.Rows.Add(row);
246+
}
247+
248+
var adaptiveCardBody = new List<AdaptiveElement>();
249+
adaptiveCardBody.Add(table);
250+
251+
engine.AddScriptOutput("AdaptiveCard", JsonConvert.SerializeObject(adaptiveCardBody));
252+
}
253+
254+
private void ReturnErrorMessage(Engine engine, string message)
255+
{
256+
var adaptiveCardBody = new List<AdaptiveElement>
257+
{
258+
new AdaptiveTextBlock(message)
259+
{
260+
Type = "TextBlock",
261+
Weight = AdaptiveTextWeight.Bolder,
262+
Size = AdaptiveTextSize.Default,
263+
},
264+
};
265+
266+
engine.AddScriptOutput("AdaptiveCard", JsonConvert.SerializeObject(adaptiveCardBody));
267+
}
268+
}
269+
270+
internal class ResourceInfo
271+
{
272+
public Resource Resource { get; set; }
273+
274+
public double Duration { get; set; }
275+
}
276+
}

0 commit comments

Comments
 (0)