Skip to content

Commit 33edcb1

Browse files
authored
Merge pull request #3 from CoreLayer/dev-0.0.3
Dev 0.0.3-2
2 parents 1292add + fb6fb3f commit 33edcb1

File tree

6 files changed

+207
-44
lines changed

6 files changed

+207
-44
lines changed

README.md

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,137 @@
1-
# CoreLayer.Citrix.Adc.NodeBackup
1+
# CoreLayer.Citrix.Adc.NodeBackup
2+
3+
4+
## 1. Introduction
5+
6+
This project allows you to automatically backup your Citrix ADC (formerly known as NetScaler) configuration to a filepath available to the client.
7+
Developed in .NetCore using C#, we provide a fully running application in several flavors:
8+
- Single-file executables for Linux, MacOS and Windows
9+
- Dotnet-dependent DLL
10+
- Linux container (https://hub.docker.com/r/corelayer/corelayer-citrix-adc-nodebackupservice)
11+
12+
---
13+
## 2. Configuration
14+
### 2.1. Prerequisites
15+
#### 2.1.1. Citrix ADC
16+
17+
For optimal security, create a separate user on Citrix ADC for backup purposes.
18+
The command policy below limits the allowed commands to the absolute minimum.
19+
20+
*Replace <username> and <password> with values of your own choosing.*
21+
22+
```text
23+
add system cmdPolicy corelayer_backup ALLOW "(^(show\\s+system\\s+backup)|(create|rm)\\s+system\\s+backup\\s+.*)|(^show\\ssystem\\sfile\\s[\\w\\.-]+\\s-fileLocation\\s\"/var/ns_sys_backup\")"
24+
25+
26+
add system user <username> <password> -externalAuth DISABLED -timeout 900 -allowedManagementInterface API
27+
bind system user backup corelayer_backup 0
28+
```
29+
30+
### 2.2. NodeBackup
31+
NodeBackup can be configured using the following options.
32+
33+
Depending on your enviroment, you may choose to provide values for these parameters in one of the following ways:
34+
35+
|Provider|Target|
36+
|---|---|
37+
|Configuration file|appsettings.json|
38+
|Environment variables|Environment variable per option|
39+
|Command-line parameters|Parameter per option|
40+
41+
#### 2.2.1. Node Configuration:
42+
43+
|Parameter|Value|Default|Description|
44+
|---|---|---|---|
45+
|Organization|CoreLayer|N/A|Organization Name|
46+
|Environment|Production|N/A|ADC Environment Name|
47+
|NodeName|nsprod|N/A|ADC Node Name|
48+
|NodeAddress|https://nsprod.prd.corelayer.local|N/A|ADC NSIP or URL|
49+
|Username|backup|N/A|Username|
50+
|Password|backup|N/A|Password|
51+
|CertificateValidation|Disabled|Enabled|Validate ADC SSL certificate (Disable for self-signed certificates|
52+
53+
#### 2.2.2. Backup Configuration:
54+
55+
|Parameter|Value|Default|Description|
56+
|---|---|---|---|
57+
|Start|00:00|N/A|Start time for backups (HH:MM)|
58+
|Interval|3600|3600|Interval in seconds, must be factor of 300 (5minutes)|
59+
|BasePath|/var/corelayer/adc/nodebackup|N/A|Base path to store backups|
60+
|CreateSubdirectoryForNode|true|true|Create a subdirectory for the node|
61+
62+
#### 2.2.3. Prometheus Configuration:
63+
64+
|Parameter|Value|Default|Description|
65+
|---|---|---|---|
66+
|Enable Metrics Server|true|true|Enable the Prometheus Metrics Endpoint|
67+
|Metrics Server Port|5000|5000|TCP Endpoint for the Metrics server|
68+
|Metrics Server Use https|false|true|Run the TCP Endpoint with https|
69+
|Metrics Name Prefix|corelayer|corelayer|Prefix for the metric names|
70+
71+
## 3. Platforms
72+
### 3.1. Docker
73+
#### 3.1.1. Docker-compose
74+
##### 3.1.1.1. Template configuration
75+
76+
- *Replace __nodename__ with the actual node name.*
77+
- *Replace the __hostport__ with the desired external port number.*
78+
79+
```yaml
80+
version: '3'
81+
services:
82+
nodename-nodebackup-service:
83+
image: corelayer/corelayer-citrix-adc-nodebackupservice:dev-latest
84+
container_name: nodename-nodebackupservice
85+
environment:
86+
- Logging__LogLevel__Default=Information
87+
- NodeBackupConfiguration__Node__OwnerName=
88+
- NodeBackupConfiguration__Node__EnvironmentName=
89+
- NodeBackupConfiguration__Node__NodeName=
90+
- NodeBackupConfiguration__Node__NodeAddress=
91+
- NodeBackupConfiguration__Node__Username=
92+
- NodeBackupConfiguration__Node__Password=
93+
- NodeBackupConfiguration__Node__CertificateValidation=
94+
- NodeBackupConfiguration__Backup__Start=
95+
- NodeBackupConfiguration__Backup__Interval=
96+
- NodeBackupConfiguration__Backup__BasePath=
97+
- NodeBackupConfiguration__Backup__CreateSubdirectoryForNode=
98+
- NodeBackupConfiguration__Prometheus__MetricsServer__Enabled=
99+
- NodeBackupConfiguration__Prometheus__MetricsServer__Port=5000
100+
- NodeBackupConfiguration__Prometheus__MetricsServer__UseHttps=
101+
- NodeBackupConfiguration__Prometheus__NamePrefix=
102+
volumes:
103+
- $PWD:/var/corelayer/adc/nodebackup
104+
ports:
105+
- "hostport:5000/tcp"
106+
```
107+
108+
#### 2.3.2 Example
109+
110+
```yaml
111+
version: '3'
112+
services:
113+
<nodename>-nodebackup-service:
114+
image: corelayer/corelayer-citrix-adc-nodebackupservice:dev-latest
115+
container_name: <nodename>-nodebackupservice
116+
environment:
117+
- Logging__LogLevel__Default=Information
118+
- NodeBackupConfiguration__Node__OwnerName=CoreLayer
119+
- NodeBackupConfiguration__Node__EnvironmentName=Production
120+
- NodeBackupConfiguration__Node__NodeName=nsprod01
121+
- NodeBackupConfiguration__Node__NodeAddress=https://nsprod01.corelayer.local
122+
- NodeBackupConfiguration__Node__Username=backup
123+
- NodeBackupConfiguration__Node__Password=backup
124+
- NodeBackupConfiguration__Node__CertificateValidation=Enabled
125+
- NodeBackupConfiguration__Backup__Start=00:00
126+
- NodeBackupConfiguration__Backup__Interval=3600
127+
- NodeBackupConfiguration__Backup__BasePath=/var/corelayer/adc/nodebackup
128+
- NodeBackupConfiguration__Backup__CreateSubdirectoryForNode=true
129+
- NodeBackupConfiguration__Prometheus__MetricsServer__Enabled=true
130+
- NodeBackupConfiguration__Prometheus__MetricsServer__Port=5000
131+
- NodeBackupConfiguration__Prometheus__MetricsServer__UseHttps=false
132+
- NodeBackupConfiguration__Prometheus__NamePrefix=corelayer
133+
volumes:
134+
- $PWD:/var/corelayer/adc/nodebackup
135+
ports:
136+
- "5000:5000/tcp"
137+
```

src/CoreLayer.Citrix.Adc.NodeBackup/CoreLayer.Citrix.Adc.NodeBackupWorker/Configuration/BackupConfiguration.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ namespace CoreLayer.Citrix.Adc.NodeBackupWorker.Configuration
55
public class BackupConfiguration
66
{
77
public DateTime Start { get; set; }
8-
public int Interval { get; set; }
8+
public int Interval { get; set; } = 3600;
99
public string BasePath { get; set; }
10-
public bool CreateSubdirectoryForNode { get; set; }
10+
public bool CreateSubdirectoryForNode { get; set; } = true;
1111
}
1212
}

src/CoreLayer.Citrix.Adc.NodeBackup/CoreLayer.Citrix.Adc.NodeBackupWorker/Configuration/NodeConfiguration.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ public class NodeConfiguration
1010
public string NodeName { get; set; }
1111

1212
public Uri NodeAddress { get; set; }
13-
public NitroHttpClientCertificateValidation CertificateValidation { get; set; }
13+
14+
public NitroHttpClientCertificateValidation CertificateValidation { get; set; } =
15+
NitroHttpClientCertificateValidation.Enabled;
1416

1517
public string Username { get; set; }
1618
public string Password { get; set; }

src/CoreLayer.Citrix.Adc.NodeBackup/CoreLayer.Citrix.Adc.NodeBackupWorker/Configuration/PrometheusConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace CoreLayer.Citrix.Adc.NodeBackupWorker.Configuration
22
{
33
public class PrometheusConfiguration
44
{
5-
public bool EnableMetricsServer { get; set; } = false;
5+
public PrometheusMetricsServerConfiguration MetricsServer { get; set; }
66
public string NamePrefix { get; set; } = "corelayer";
77
}
88
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace CoreLayer.Citrix.Adc.NodeBackupWorker.Configuration
2+
{
3+
public class PrometheusMetricsServerConfiguration
4+
{
5+
public bool Enabled { get; set; }
6+
public int Port { get; set; } = 5000;
7+
public bool UseHttps { get; set; } = false;
8+
}
9+
}

src/CoreLayer.Citrix.Adc.NodeBackup/CoreLayer.Citrix.Adc.NodeBackupWorker/NodeBackupHostedService.cs

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public NodeBackupHostedService(IHostApplicationLifetime hostApplicationLifetime,
5959
// Configure timer
6060
_logger.LogDebug("Configuring NodeBackup Timer");
6161
_timer = new System.Timers.Timer(_nodeBackupConfiguration.Backup.Interval * 1000);
62-
_timer.Elapsed += TriggerNodeBackup;
62+
_timer.Elapsed += OnBackupTimerElapsed;
6363

6464
// Configure output
6565
_logger.LogDebug("Configuring Output path");
@@ -89,9 +89,12 @@ public NodeBackupHostedService(IHostApplicationLifetime hostApplicationLifetime,
8989
hostApplicationLifetime.StopApplication();
9090

9191
// Start embedded Prometheus MetricsServer
92-
if (!_nodeBackupConfiguration.Prometheus.EnableMetricsServer) return;
92+
if (!_nodeBackupConfiguration.Prometheus.MetricsServer.Enabled) return;
9393
_logger.LogInformation("Starting metrics server");
94-
_metricServer = new MetricServer(hostname: "localhost", port: 5001, useHttps: false);
94+
_metricServer = new MetricServer(
95+
hostname: "localhost",
96+
port: _nodeBackupConfiguration.Prometheus.MetricsServer.Port,
97+
useHttps: _nodeBackupConfiguration.Prometheus.MetricsServer.UseHttps);
9598
_metricServer.Start();
9699
}
97100

@@ -222,82 +225,95 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken)
222225
_logger.LogDebug("Waiting 5 seconds before starting countdown");
223226
await Task.Delay(5000, cancellationToken);
224227

225-
await TriggerExecutionTimer(cancellationToken);
228+
await ExecutionLoopAsync(cancellationToken);
226229

227230
_logger.LogInformation("Stopping backup timer");
228231
_timer.Stop();
229232
}
230233

231-
private async Task TriggerExecutionTimer(CancellationToken cancellationToken)
234+
private async Task ExecutionLoopAsync(CancellationToken cancellationToken)
232235
{
233236
while (!cancellationToken.IsCancellationRequested)
234237
{
235-
var nextIntervalCountdown = CalculateNextTargetSeconds();
238+
var nextIntervalSeconds = CalculateNextIntervalSeconds();
236239

237240
if (!_timer.Enabled)
238241
{
239-
if (nextIntervalCountdown == 300)
242+
if (nextIntervalSeconds == _nodeBackupConfiguration.Backup.Interval)
240243
{
241244
_logger.LogInformation("Starting backup timer");
242245
_timer.Enabled = true;
243246
await ExecuteBackupProcedure(DateTime.Now);
244247
}
245248
else
246249
{
247-
_logger.LogInformation("Counting down to backup timer start: {0}",
248-
nextIntervalCountdown.ToString());
250+
_logger.LogInformation("Counting down to backup timer start: {0} at {1} - {2} seconds",
251+
TimeSpan.FromSeconds(nextIntervalSeconds).ToString(@"hh\:mm\:ss"),
252+
TimeSpan.FromSeconds(DateTime.Now.Ticks / TimeSpan.TicksPerSecond + nextIntervalSeconds).ToString(@"hh\:mm\:ss"),
253+
nextIntervalSeconds.ToString());
249254
}
250255
}
251256
else
252257
{
253-
_logger.LogDebug("Counting down to next backup: {0}", nextIntervalCountdown.ToString());
258+
_logger.LogInformation("Counting down to next backup: {0} at {1} - {2} seconds",
259+
TimeSpan.FromSeconds(nextIntervalSeconds).ToString(@"hh\:mm\:ss"),
260+
TimeSpan.FromSeconds(DateTime.Now.Ticks / TimeSpan.TicksPerSecond + nextIntervalSeconds).ToString(@"hh\:mm\:ss"),
261+
nextIntervalSeconds.ToString());
254262
}
263+
255264
await Task.Delay(1000, cancellationToken);
256265
}
257266
}
258267

259-
private long CalculateNextTargetSeconds()
268+
private long CalculateNextIntervalSeconds()
260269
{
261-
long nextTargetSeconds;
262-
263-
if (DateTime.Now.Ticks / TimeSpan.TicksPerSecond < _nodeBackupConfiguration.Backup.Start.Ticks / TimeSpan.TicksPerSecond)
264-
{
265-
_logger.LogDebug("Current time is before target start time");
266-
nextTargetSeconds = ((_nodeBackupConfiguration.Backup.Start.Ticks - DateTime.Now.Ticks) /
267-
TimeSpan.TicksPerSecond);
268-
269-
if (nextTargetSeconds > _nodeBackupConfiguration.Backup.Interval)
270-
{
271-
nextTargetSeconds -= _nodeBackupConfiguration.Backup.Interval;
270+
return
271+
DateTime.Now.Ticks / TimeSpan.TicksPerSecond < _nodeBackupConfiguration.Backup.Start.Ticks / TimeSpan.TicksPerSecond
272+
? CalculateIntervalSecondsFromNowToBackupStart() : CalculateIntervalSecondsFromBackupStartToNow();
273+
}
272274

273-
_logger.LogDebug(
274-
"Seconds to next interval : {0}",
275-
nextTargetSeconds.ToString());
276-
}
275+
private long CalculateIntervalSecondsFromBackupStartToNow()
276+
{
277+
_logger.LogDebug("Current time is later than target start time");
278+
var nextTargetSeconds = ((DateTime.Now.Ticks - _nodeBackupConfiguration.Backup.Start.Ticks) /
279+
TimeSpan.TicksPerSecond);
277280

278-
nextTargetSeconds++;
279-
}
281+
if (nextTargetSeconds < 0)
282+
nextTargetSeconds = _nodeBackupConfiguration.Backup.Interval - nextTargetSeconds;
280283
else
281284
{
282-
_logger.LogDebug("Current time is later than target start time");
283-
nextTargetSeconds = ((DateTime.Now.Ticks - _nodeBackupConfiguration.Backup.Start.Ticks) /
284-
TimeSpan.TicksPerSecond);
285+
nextTargetSeconds = _nodeBackupConfiguration.Backup.Interval -
286+
nextTargetSeconds % _nodeBackupConfiguration.Backup.Interval;
287+
}
288+
289+
_logger.LogDebug(
290+
"Seconds to next interval : {0}",
291+
nextTargetSeconds.ToString());
292+
293+
return nextTargetSeconds;
294+
}
295+
296+
private long CalculateIntervalSecondsFromNowToBackupStart()
297+
{
298+
_logger.LogDebug("Current time is before target start time");
299+
var nextTargetSeconds = (_nodeBackupConfiguration.Backup.Start.Ticks - DateTime.Now.Ticks) /
300+
TimeSpan.TicksPerSecond;
301+
302+
if (nextTargetSeconds > _nodeBackupConfiguration.Backup.Interval)
303+
{
304+
nextTargetSeconds -= _nodeBackupConfiguration.Backup.Interval;
285305

286-
if (nextTargetSeconds < 0)
287-
nextTargetSeconds = 300 - nextTargetSeconds;
288-
else
289-
{
290-
nextTargetSeconds = _nodeBackupConfiguration.Backup.Interval - nextTargetSeconds % _nodeBackupConfiguration.Backup.Interval;
291-
}
292306
_logger.LogDebug(
293307
"Seconds to next interval : {0}",
294308
nextTargetSeconds.ToString());
295309
}
296-
310+
311+
nextTargetSeconds++;
312+
297313
return nextTargetSeconds;
298314
}
299315

300-
private async void TriggerNodeBackup(Object source, ElapsedEventArgs e)
316+
private async void OnBackupTimerElapsed(Object source, ElapsedEventArgs e)
301317
{
302318
_logger.LogInformation("Backup was triggered at {0:HH:mm:ss}",
303319
e.SignalTime);

0 commit comments

Comments
 (0)