зеркало из https://github.com/Azure/iotedge.git
NetworkController module (#2040)
* NetworkController linux implementation
This commit is contained in:
Родитель
7a5d9017da
Коммит
c51606d21d
|
@ -207,6 +207,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{F921
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestResultCoordinator", "test\modules\TestResultCoordinator\TestResultCoordinator.csproj", "{8181EB49-62CE-495B-8078-08DCF8C30541}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "connectivity", "connectivity", "{F3B989E5-E7F5-4A07-AEA1-84B63040A190}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkController", "test\connectivity\modules\NetworkController\NetworkController.csproj", "{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
CheckInBuild|Any CPU = CheckInBuild|Any CPU
|
||||
|
@ -671,6 +675,14 @@ Global
|
|||
{8181EB49-62CE-495B-8078-08DCF8C30541}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8181EB49-62CE-495B-8078-08DCF8C30541}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8181EB49-62CE-495B-8078-08DCF8C30541}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.CheckInBuild|Any CPU.ActiveCfg = CheckInBuild|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.CheckInBuild|Any CPU.Build.0 = CheckInBuild|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.CodeCoverage|Any CPU.ActiveCfg = CheckInBuild|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.CodeCoverage|Any CPU.Build.0 = CheckInBuild|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -751,6 +763,8 @@ Global
|
|||
{C1FDA6CF-7EF6-4C3F-81E7-4C13EE399BF1} = {69157D2B-2AD3-4EE0-A837-6AD9329EDDC3}
|
||||
{F921339B-32F9-4BF3-B364-2DB01FA2F1A1} = {2300ED4C-1D5A-460F-8691-7C85E1162E0C}
|
||||
{8181EB49-62CE-495B-8078-08DCF8C30541} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
|
||||
{F3B989E5-E7F5-4A07-AEA1-84B63040A190} = {2300ED4C-1D5A-460F-8691-7C85E1162E0C}
|
||||
{9DB94B1F-D773-4D17-9CD7-54EE5DD3F84B} = {F3B989E5-E7F5-4A07-AEA1-84B63040A190}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D71830F5-3AF5-46B4-8A9E-1DCE4F2253AC}
|
||||
|
|
|
@ -243,6 +243,13 @@ jobs:
|
|||
name: Relayer
|
||||
imageName: azureiotedge-relayer
|
||||
project: Relayer
|
||||
|
||||
# Network Controller
|
||||
- template: templates/image-linux.yaml
|
||||
parameters:
|
||||
name: Network Controller
|
||||
imageName: azureiotedge-network-controller
|
||||
project: NetworkController
|
||||
|
||||
# TestResultCoordinator
|
||||
- template: templates/image-linux.yaml
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
"value": "mqtt"
|
||||
},
|
||||
"outputName": {
|
||||
"value": "output2"
|
||||
"value": "output2"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
@ -118,30 +118,37 @@
|
|||
"value": "mqtt"
|
||||
},
|
||||
"inputName": {
|
||||
"value": "input2"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"value": "input2"
|
||||
},
|
||||
"image": "<Container_Registry>/microsoft/azureiotedge-relayer:<Build.BuildNumber>-linux-<Architecture>",
|
||||
"createOptions": ""
|
||||
},
|
||||
"networkController": {
|
||||
"version": "1.0",
|
||||
"type": "docker",
|
||||
"status": "running",
|
||||
"restartPolicy": "always",
|
||||
"settings": {
|
||||
"image": "<Container_Registry>/microsoft/azureiotedge-network-controller:<Build.BuildNumber>-linux-<Architecture>",
|
||||
"createOptions": "{\"HostConfig\":{\"Privileged\":\"true\",\"NetworkMode\":\"host\",\"Binds\":[\"/var/run/docker.sock:/var/run/docker.sock\"]},\"NetworkingConfig\":{\"EndpointsConfig\":{\"host\":{}}}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$edgeHub": {
|
||||
"properties.desired": {
|
||||
"schemaVersion": "1.0",
|
||||
"routes": {
|
||||
},
|
||||
"$edgeHub": {
|
||||
"properties.desired": {
|
||||
"schemaVersion": "1.0",
|
||||
"routes": {
|
||||
"loadGen1ToRelayer1": "FROM /messages/modules/loadGen1/outputs/output1 INTO BrokeredEndpoint(\"/modules/relayer1/inputs/input1\")",
|
||||
"relayer1ToCloud": "FROM /messages/modules/relayer1/outputs/output1 INTO $upstream",
|
||||
"loadGen2ToRelayer2": "FROM /messages/modules/loadGen2/outputs/output2 INTO BrokeredEndpoint(\"/modules/relayer2/inputs/input2\")",
|
||||
"relayer2ToCloud": "FROM /messages/modules/relayer2/outputs/output2 INTO $upstream"
|
||||
},
|
||||
"storeAndForwardConfiguration": {
|
||||
"timeToLiveSecs": 86400
|
||||
"relayer1ToCloud": "FROM /messages/modules/relayer1/outputs/output1 INTO $upstream",
|
||||
"loadGen2ToRelayer2": "FROM /messages/modules/loadGen2/outputs/output2 INTO BrokeredEndpoint(\"/modules/relayer2/inputs/input2\")",
|
||||
"relayer2ToCloud": "FROM /messages/modules/relayer2/outputs/output2 INTO $upstream"
|
||||
},
|
||||
"storeAndForwardConfiguration": {
|
||||
"timeToLiveSecs": 86400
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -256,6 +256,7 @@ publish_app "ModuleRestarter"
|
|||
publish_app "TwinTester"
|
||||
publish_app "Relayer"
|
||||
publish_app "TestResultCoordinator"
|
||||
publish_app "NetworkController"
|
||||
|
||||
publish_lib "Microsoft.Azure.WebJobs.Extensions.EdgeHub"
|
||||
publish_lib "EdgeHubTriggerCSharp"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Connectivity tests
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[Serializable]
|
||||
class CommandExecutionException : Exception
|
||||
{
|
||||
public CommandExecutionException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandExecutionException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandExecutionException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected CommandExecutionException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RunProcessAsTask;
|
||||
|
||||
class CommandExecutor
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<CommandExecutor>();
|
||||
|
||||
public static async Task<string> Execute(string commandName, string args, CancellationToken cs)
|
||||
{
|
||||
var info = new ProcessStartInfo
|
||||
{
|
||||
FileName = commandName,
|
||||
Arguments = args
|
||||
};
|
||||
|
||||
using (ProcessResults result = await ProcessEx.RunAsync(info, cs))
|
||||
{
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
string errorMessage = $"{commandName} result: {result.StandardError.Join(" ")}";
|
||||
Log.LogError(errorMessage);
|
||||
throw new CommandExecutionException(errorMessage);
|
||||
}
|
||||
|
||||
Log.LogDebug($"{commandName} result: {result.StandardOutput.Join(" ")}");
|
||||
return result.StandardOutput.Join(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class CountedTaskExecutor
|
||||
{
|
||||
readonly TimeSpan interval;
|
||||
readonly ILogger logger;
|
||||
readonly string operationName;
|
||||
readonly int runCount;
|
||||
readonly TimeSpan startAfter;
|
||||
readonly Func<CancellationToken, Task> work;
|
||||
|
||||
public CountedTaskExecutor(
|
||||
Func<CancellationToken, Task> work,
|
||||
TimeSpan startAfter,
|
||||
TimeSpan interval,
|
||||
int runsCount,
|
||||
ILogger logger,
|
||||
string operationName)
|
||||
{
|
||||
this.startAfter = startAfter;
|
||||
this.interval = interval;
|
||||
this.runCount = runsCount;
|
||||
this.work = work;
|
||||
this.logger = logger;
|
||||
this.operationName = operationName;
|
||||
}
|
||||
|
||||
public async Task Schedule(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(this.startAfter, cancellationToken);
|
||||
|
||||
for (int i = 0; i < this.runCount; i++)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation($"Starting operation {this.operationName} run number {i}...");
|
||||
await this.work(cancellationToken);
|
||||
this.logger.LogInformation($"Successfully completed operation {this.operationName} run number {i}");
|
||||
await Task.Delay(this.interval, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.logger.LogError(e, $"Failed to run {this.operationName} scheduled task run number {i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Net.NetworkInformation;
|
||||
using Docker.DotNet;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class DockerHelper
|
||||
{
|
||||
const int DockerNetworkInterfaceNameLenght = 12;
|
||||
const int DockerNetworkInterfaceStartIndex = 0;
|
||||
const string DockerNetworkInterfaceSuffix = "br-";
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<DockerHelper>();
|
||||
|
||||
public static Option<string> GetDockerInterfaceName()
|
||||
{
|
||||
var client = new DockerClientConfiguration(new Uri(Settings.Current.DockerUri)).CreateClient();
|
||||
var networkId = client.Networks.InspectNetworkAsync(Settings.Current.NetworkId).Result.ID;
|
||||
|
||||
string interfaceName = GetInterfaceNameFromNetworkId(networkId);
|
||||
|
||||
Log.LogInformation($"{Settings.Current.NetworkId} network has id {networkId}");
|
||||
|
||||
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (item.Name.Equals(interfaceName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Log.LogInformation($"Found network interface {item.Name}");
|
||||
return Option.Some(item.Name);
|
||||
}
|
||||
}
|
||||
|
||||
Log.LogInformation($"No network interface found for docker network id {Settings.Current.NetworkId}");
|
||||
return Option.None<string>();
|
||||
}
|
||||
|
||||
static string GetInterfaceNameFromNetworkId(string networkId)
|
||||
{
|
||||
// TODO: this is on linux, windows might be different
|
||||
// Network interface name has by default first chars form networkId
|
||||
return string.Format($"{DockerNetworkInterfaceSuffix}{networkId.Substring(DockerNetworkInterfaceStartIndex, DockerNetworkInterfaceNameLenght)}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class FirewallOfflineController : IController
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<FirewallOfflineController>();
|
||||
readonly IFirewallCommands networkCommands;
|
||||
|
||||
public FirewallOfflineController(string networkInterfaceName)
|
||||
{
|
||||
Preconditions.CheckNonWhiteSpace(networkInterfaceName, nameof(networkInterfaceName));
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
this.networkCommands = new WindowsFirewallCommands(networkInterfaceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.networkCommands = new LinuxFirewallCommands(networkInterfaceName);
|
||||
}
|
||||
}
|
||||
|
||||
public string Description => "FirewallOffline";
|
||||
|
||||
public async Task<NetworkStatus> GetStatus(CancellationToken cs) => await this.networkCommands.GetStatus(cs);
|
||||
|
||||
public Task<bool> SetStatus(NetworkStatus status, CancellationToken cs)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case NetworkStatus.Restricted:
|
||||
return this.AddNetworkControllingRule(cs);
|
||||
case NetworkStatus.Default:
|
||||
return this.RemoveNetworkControllingRule(cs);
|
||||
default:
|
||||
Log.LogDebug($"Status not set {status}");
|
||||
throw new NotSupportedException($"Status is not supported {status}");
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> AddNetworkControllingRule(CancellationToken cs)
|
||||
{
|
||||
bool result = await this.networkCommands.AddDropRule(cs);
|
||||
|
||||
NetworkStatus status = await this.GetStatus(cs);
|
||||
Log.LogInformation($"Command AddDropRule execution success {result}, network status {status}");
|
||||
|
||||
return result && status == NetworkStatus.Restricted;
|
||||
}
|
||||
|
||||
async Task<bool> RemoveNetworkControllingRule(CancellationToken cs)
|
||||
{
|
||||
bool result = await this.networkCommands.RemoveDropRule(cs);
|
||||
|
||||
NetworkStatus status = await this.GetStatus(cs);
|
||||
Log.LogInformation($"Command RemoveDropRule execution success {result}, network status {status}");
|
||||
|
||||
return result && status == NetworkStatus.Default;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
interface IController
|
||||
{
|
||||
string Description { get; }
|
||||
|
||||
Task<bool> SetStatus(NetworkStatus status, CancellationToken cs);
|
||||
|
||||
Task<NetworkStatus> GetStatus(CancellationToken cs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
interface IFirewallCommands
|
||||
{
|
||||
Task<bool> RemoveDropRule(CancellationToken cs);
|
||||
|
||||
Task<bool> AddDropRule(CancellationToken cs);
|
||||
|
||||
Task<NetworkStatus> GetStatus(CancellationToken cs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
interface INetworkInterfaceCommands
|
||||
{
|
||||
Task<bool> Enable(CancellationToken token);
|
||||
|
||||
Task<bool> Disable(CancellationToken token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
|
||||
interface INetworkStatusReporter
|
||||
{
|
||||
Task ReportNetworkStatus(NetworkControllerOperation settingRule, NetworkStatus status, string description, bool success = true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class LinuxFirewallCommands : IFirewallCommands
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<LinuxFirewallCommands>();
|
||||
readonly string networkInterfaceName;
|
||||
|
||||
public LinuxFirewallCommands(string networkInterfaceName)
|
||||
{
|
||||
this.networkInterfaceName = networkInterfaceName;
|
||||
}
|
||||
|
||||
public async Task<NetworkStatus> GetStatus(CancellationToken cs)
|
||||
{
|
||||
try
|
||||
{
|
||||
string output = await CommandExecutor.Execute(
|
||||
"tc",
|
||||
$"qdisc show dev {this.networkInterfaceName}",
|
||||
cs);
|
||||
|
||||
// parse output to see if online or offline
|
||||
if (output.Contains("qdisc noqueue"))
|
||||
{
|
||||
return NetworkStatus.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NetworkStatus.Restricted;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Failed to get network status");
|
||||
return NetworkStatus.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveDropRule(CancellationToken cs)
|
||||
{
|
||||
try
|
||||
{
|
||||
string output = await CommandExecutor.Execute(
|
||||
"tc",
|
||||
$"qdisc delete dev {this.networkInterfaceName} root netem loss 100%",
|
||||
cs);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Failed to set accept rule");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> AddDropRule(CancellationToken cs)
|
||||
{
|
||||
try
|
||||
{
|
||||
string output = await CommandExecutor.Execute(
|
||||
"tc",
|
||||
$"qdisc add dev {this.networkInterfaceName} root netem loss 100%",
|
||||
cs);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Failed to set drop rule");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class LinuxNetworkInterfaceCommands : INetworkInterfaceCommands
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<LinuxNetworkInterfaceCommands>();
|
||||
readonly string interfaceName;
|
||||
|
||||
public LinuxNetworkInterfaceCommands(string interfaceName)
|
||||
{
|
||||
this.interfaceName = interfaceName;
|
||||
}
|
||||
|
||||
public async Task<bool> Disable(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
string output = await CommandExecutor.Execute("ifconfig", $"{this.interfaceName} down", token);
|
||||
Log.LogInformation($"Disabled {this.interfaceName}");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Failed to disable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> Enable(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exitCode = await CommandExecutor.Execute("ifconfig", $"{this.interfaceName} up", token);
|
||||
Log.LogInformation($"Enabled {this.interfaceName}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(e, "Failed to disable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup Condition="'$(DotNet_Runtime)' != 'netcoreapp3.0'">
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(OS)|$(DotNet_Runtime)' == 'Unix|netcoreapp3.0'">
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Configurations>Debug;Release;CheckInBuild</Configurations>
|
||||
<HighEntropyVA>true</HighEntropyVA>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="docker\linux\amd64\Dockerfile" />
|
||||
<None Remove="docker\linux\arm32v7\Dockerfile" />
|
||||
<None Remove="docker\linux\arm64v8\Dockerfile" />
|
||||
<None Remove="docker\windows\amd64\Dockerfile" />
|
||||
<None Remove="docker\windows\arm32v7\Dockerfile" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="docker*/**/*.*" CopyToPublishDirectory="Always" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Docker.DotNet" Version="3.125.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.1" />
|
||||
<PackageReference Include="RunProcessAsTask" Version="1.2.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="config\settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\..\..\..\stylecop.json" Link="stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\edge-util\src\Microsoft.Azure.Devices.Edge.Util\Microsoft.Azure.Devices.Edge.Util.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>..\..\..\..\stylecop.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\stylecop.props" />
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
enum NetworkControllerMode
|
||||
{
|
||||
OfflineNetworkInterface = 0,
|
||||
OfflineTrafficController,
|
||||
SatelliteTrafficController
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
enum NetworkControllerOperation
|
||||
{
|
||||
SettingRule,
|
||||
RuleSet
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class NetworkInterfaceOfflineController : IController
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<NetworkInterfaceOfflineController>();
|
||||
|
||||
readonly INetworkInterfaceCommands networkCommands;
|
||||
readonly string networkInterfaceName;
|
||||
|
||||
public NetworkInterfaceOfflineController(string dockerInterfaceName)
|
||||
{
|
||||
this.networkInterfaceName =
|
||||
Preconditions.CheckNonWhiteSpace(dockerInterfaceName, nameof(dockerInterfaceName));
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
this.networkCommands = new WindowsNetworkInterfaceCommands();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.networkCommands = new LinuxNetworkInterfaceCommands(this.networkInterfaceName);
|
||||
}
|
||||
}
|
||||
|
||||
public string Description => "NetworkInterfaceOffline";
|
||||
|
||||
public Task<NetworkStatus> GetStatus(CancellationToken cs)
|
||||
{
|
||||
OperationalStatus status = this.GetNetworkInterfaceStatus();
|
||||
switch (status)
|
||||
{
|
||||
case OperationalStatus.Up:
|
||||
return Task.FromResult(NetworkStatus.Default);
|
||||
case OperationalStatus.Unknown:
|
||||
return Task.FromResult(NetworkStatus.Unknown);
|
||||
default:
|
||||
return Task.FromResult(NetworkStatus.Restricted);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> AddNetworkControllingRule(CancellationToken cs)
|
||||
{
|
||||
bool result = await this.networkCommands.Disable(cs);
|
||||
NetworkStatus status = await this.GetStatus(cs);
|
||||
Log.LogInformation($"Command AddNetworkControllingRule success {result}, network status {status}");
|
||||
|
||||
return result && status == NetworkStatus.Restricted;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveNetworkControllingRule(CancellationToken cs)
|
||||
{
|
||||
bool result = await this.networkCommands.Enable(cs);
|
||||
NetworkStatus status = await this.GetStatus(cs);
|
||||
Log.LogInformation($"Command RemoveNetworkControllingRule execution success {result}, network status {status}");
|
||||
|
||||
return result && status == NetworkStatus.Default;
|
||||
}
|
||||
|
||||
public Task<bool> SetStatus(NetworkStatus status, CancellationToken cs)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case NetworkStatus.Restricted:
|
||||
return this.AddNetworkControllingRule(cs);
|
||||
case NetworkStatus.Default:
|
||||
return this.RemoveNetworkControllingRule(cs);
|
||||
default:
|
||||
Log.LogDebug($"Status not set {status}");
|
||||
throw new NotSupportedException($"Status '{status}' is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
OperationalStatus GetNetworkInterfaceStatus()
|
||||
{
|
||||
foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (item.Name.Equals(this.networkInterfaceName))
|
||||
{
|
||||
return item.OperationalStatus;
|
||||
}
|
||||
}
|
||||
|
||||
return OperationalStatus.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class NetworkReporter : INetworkStatusReporter
|
||||
{
|
||||
public Task ReportNetworkStatus(NetworkControllerOperation settingRule, NetworkStatus status, string description, bool success = true)
|
||||
{
|
||||
// TODO: send to analyzer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
enum NetworkStatus
|
||||
{
|
||||
Unknown = 0,
|
||||
Restricted,
|
||||
Default
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
class Program
|
||||
{
|
||||
static readonly ILogger Log = Logger.Factory.CreateLogger<Program>();
|
||||
|
||||
static async Task Main()
|
||||
{
|
||||
(CancellationTokenSource cts, ManualResetEventSlim completed, Option<object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Log);
|
||||
|
||||
Log.LogInformation($"Starting with {Settings.Current.NetworkControllerMode}");
|
||||
|
||||
var networkInterfaceName = DockerHelper.GetDockerInterfaceName();
|
||||
if (networkInterfaceName.HasValue)
|
||||
{
|
||||
await networkInterfaceName.ForEachAsync(
|
||||
async name =>
|
||||
{
|
||||
var nic = new NetworkInterfaceOfflineController(name);
|
||||
var firewall = new FirewallOfflineController(name);
|
||||
var satellite = new SatelliteController(name);
|
||||
var controllers = new List<IController>() { nic, firewall, satellite };
|
||||
await RemoveAllControllingRules(controllers, cts.Token);
|
||||
|
||||
switch (Settings.Current.NetworkControllerMode)
|
||||
{
|
||||
case NetworkControllerMode.OfflineNetworkInterface:
|
||||
await StartAsync(nic, cts.Token);
|
||||
break;
|
||||
case NetworkControllerMode.OfflineTrafficController:
|
||||
await StartAsync(firewall, cts.Token);
|
||||
break;
|
||||
case NetworkControllerMode.SatelliteTrafficController:
|
||||
await StartAsync(satellite, cts.Token);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
await cts.Token.WhenCanceled();
|
||||
completed.Set();
|
||||
handler.ForEach(h => GC.KeepAlive(h));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.LogError($"No network interface found for docker network {Settings.Current.NetworkId}");
|
||||
}
|
||||
}
|
||||
|
||||
static async Task StartAsync(IController controller, CancellationToken cancellationToken)
|
||||
{
|
||||
var delay = Settings.Current.StartAfter;
|
||||
|
||||
INetworkStatusReporter reporter = new NetworkReporter();
|
||||
foreach (Frequency item in Settings.Current.Frequencies)
|
||||
{
|
||||
Log.LogInformation($"Schedule task with {controller.Description} to start after {delay} Offline frequency {item.OfflineFrequency} Online frequency {item.OnlineFrequency} Run times {item.RunsCount}");
|
||||
|
||||
var taskExecutor = new CountedTaskExecutor(
|
||||
async cs =>
|
||||
{
|
||||
await SetNetworkStatus(controller, NetworkStatus.Restricted, reporter, cs);
|
||||
await Task.Delay(item.OfflineFrequency, cs);
|
||||
await SetNetworkStatus(controller, NetworkStatus.Default, reporter, cs);
|
||||
},
|
||||
delay,
|
||||
item.OfflineFrequency,
|
||||
item.RunsCount,
|
||||
Log,
|
||||
"restrict/default");
|
||||
|
||||
await taskExecutor.Schedule(cancellationToken);
|
||||
|
||||
// Only needs to set the start delay for first frequency, after that reset to 0
|
||||
delay = TimeSpan.FromSeconds(0);
|
||||
}
|
||||
}
|
||||
|
||||
static async Task SetNetworkStatus(IController controller, NetworkStatus status, INetworkStatusReporter reporter, CancellationToken cs)
|
||||
{
|
||||
await reporter.ReportNetworkStatus(NetworkControllerOperation.SettingRule, status, controller.Description);
|
||||
bool success = await controller.SetStatus(status, cs);
|
||||
await reporter.ReportNetworkStatus(NetworkControllerOperation.RuleSet, status, controller.Description, success);
|
||||
}
|
||||
|
||||
static async Task RemoveAllControllingRules(IList<IController> controllerList, CancellationToken cancellationToken)
|
||||
{
|
||||
var reporter = new NetworkReporter();
|
||||
|
||||
foreach (var controller in controllerList)
|
||||
{
|
||||
NetworkStatus status = await controller.GetStatus(cancellationToken);
|
||||
if (status != NetworkStatus.Default)
|
||||
{
|
||||
Log.LogInformation($"Network is {status} with {controller.Description}, setting default");
|
||||
bool online = await controller.SetStatus(NetworkStatus.Default, cancellationToken);
|
||||
if (!online)
|
||||
{
|
||||
Log.LogError($"Failed to ensure it starts with default values.");
|
||||
throw new TestInitializationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.LogInformation($"Network is online");
|
||||
await reporter.ReportNetworkStatus(NetworkControllerOperation.RuleSet, NetworkStatus.Default, "All", true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// TODO: implement satellite
|
||||
class SatelliteController : IController
|
||||
{
|
||||
string name;
|
||||
|
||||
public SatelliteController(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public string Description => "Satellite";
|
||||
|
||||
public Task<bool> SetStatus(NetworkStatus status, CancellationToken cs)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<NetworkStatus> GetStatus(CancellationToken cs)
|
||||
{
|
||||
return Task.FromResult(NetworkStatus.Default);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
class Settings
|
||||
{
|
||||
const string StartAfterPropertyName = "StartAfter";
|
||||
const string DockerUriPropertyName = "DockerUri";
|
||||
const string FrequencyPropertyName = "RunFrequencies";
|
||||
const string NetworkIdPropertyName = "NetworkId";
|
||||
const string NetworkControllerModePropertyName = "NetworkControllerMode";
|
||||
|
||||
static readonly Lazy<Settings> Setting = new Lazy<Settings>(
|
||||
() =>
|
||||
{
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("config/settings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var result = new List<Frequency>();
|
||||
configuration.GetSection(FrequencyPropertyName).Bind(result);
|
||||
|
||||
return new Settings(
|
||||
configuration.GetValue<TimeSpan>(StartAfterPropertyName),
|
||||
configuration.GetValue<string>(DockerUriPropertyName),
|
||||
configuration.GetValue<string>(NetworkIdPropertyName),
|
||||
result,
|
||||
configuration.GetValue<NetworkControllerMode>(NetworkControllerModePropertyName));
|
||||
});
|
||||
|
||||
Settings(
|
||||
TimeSpan startAfter,
|
||||
string dockerUri,
|
||||
string networkId,
|
||||
IList<Frequency> frequencies,
|
||||
NetworkControllerMode mode)
|
||||
{
|
||||
this.StartAfter = startAfter;
|
||||
this.Frequencies = frequencies;
|
||||
this.DockerUri = Preconditions.CheckNonWhiteSpace(dockerUri, nameof(dockerUri));
|
||||
this.NetworkId = Preconditions.CheckNonWhiteSpace(networkId, nameof(networkId));
|
||||
this.NetworkControllerMode = mode;
|
||||
}
|
||||
|
||||
public static Settings Current => Setting.Value;
|
||||
|
||||
public TimeSpan StartAfter { get; }
|
||||
|
||||
public IList<Frequency> Frequencies { get; }
|
||||
|
||||
public string DockerUri { get; }
|
||||
|
||||
public string NetworkId { get; }
|
||||
|
||||
public NetworkControllerMode NetworkControllerMode { get; }
|
||||
}
|
||||
|
||||
class Frequency
|
||||
{
|
||||
public TimeSpan OfflineFrequency { get; set; }
|
||||
|
||||
public TimeSpan OnlineFrequency { get; set; }
|
||||
|
||||
public int RunsCount { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[Serializable]
|
||||
class TestInitializationException : Exception
|
||||
{
|
||||
public TestInitializationException()
|
||||
{
|
||||
}
|
||||
|
||||
public TestInitializationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public TestInitializationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected TestInitializationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices.Edge.Util;
|
||||
|
||||
class WindowsFirewallCommands : IFirewallCommands
|
||||
{
|
||||
readonly string networkInterfaceName;
|
||||
|
||||
public WindowsFirewallCommands(string networkInterfaceName)
|
||||
{
|
||||
this.networkInterfaceName =
|
||||
Preconditions.CheckNonWhiteSpace(networkInterfaceName, nameof(networkInterfaceName));
|
||||
}
|
||||
|
||||
public Task<NetworkStatus> GetStatus(CancellationToken cs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> RemoveDropRule(CancellationToken cs) => throw new NotImplementedException();
|
||||
|
||||
public Task<bool> AddDropRule(CancellationToken cs) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
namespace NetworkController
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
class WindowsNetworkInterfaceCommands : INetworkInterfaceCommands
|
||||
{
|
||||
public Task<bool> Disable(CancellationToken token) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public Task<bool> Enable(CancellationToken token) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"StartAfter": "00:05:00",
|
||||
"DockerUri": "unix:///var/run/docker.sock",
|
||||
"NetworkId": "azure-iot-edge",
|
||||
"NetworkControllerMode": "OfflineTrafficController",
|
||||
"RunFrequencies": [
|
||||
{
|
||||
"OfflineFrequency": "00:05:00",
|
||||
"OnlineFrequency": "00:05:00",
|
||||
"RunsCount": "6"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
ARG base_tag=2.1.13-alpine3.10
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime:${base_tag}
|
||||
|
||||
ARG EXE_DIR=.
|
||||
|
||||
RUN apk update && \
|
||||
apk add --no-cache iproute2
|
||||
|
||||
ENV MODULE_NAME "NetworkController.dll"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY $EXE_DIR/ ./
|
||||
|
||||
CMD echo "$(date --utc +"[%Y-%m-%d %H:%M:%S %:z]"): Starting Module" && \
|
||||
exec /usr/bin/dotnet NetworkController.dll
|
|
@ -0,0 +1,13 @@
|
|||
ARG base_tag=1.0.0-linux-arm32v7
|
||||
FROM azureiotedge/azureiotedge-networkcontroller-module-base:${base_tag}
|
||||
|
||||
ARG EXE_DIR=.
|
||||
|
||||
ENV MODULE_NAME "NetworkController.dll"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY $EXE_DIR/ ./
|
||||
|
||||
CMD echo "$(date --utc +"[%Y-%m-%d %H:%M:%S %:z]"): Starting Module" && \
|
||||
exec /usr/bin/dotnet NetworkController.dll
|
|
@ -0,0 +1,4 @@
|
|||
ARG base_tag=2.1.13-bionic-arm32v7
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime:${base_tag}
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends iproute2 net-tools
|
|
@ -0,0 +1,13 @@
|
|||
ARG base_tag=1.0.3-linux-arm64v8
|
||||
FROM azureiotedge/azureiotedge-module-base:${base_tag}
|
||||
|
||||
ARG EXE_DIR=.
|
||||
|
||||
ENV MODULE_NAME "NetworkController.dll"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY $EXE_DIR/ ./
|
||||
|
||||
CMD echo "$(date --utc +"%Y-%m-%d %H:%M:%S %:z") Starting Module" && \
|
||||
exec /usr/bin/dotnet NetworkController.dll
|
|
@ -0,0 +1,10 @@
|
|||
ARG base_tag=2.1.13-nanoserver-1809
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime:${base_tag}
|
||||
|
||||
ARG EXE_DIR=.
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY $EXE_DIR/ ./
|
||||
|
||||
CMD ["dotnet", "NetworkController.dll"]
|
|
@ -0,0 +1,11 @@
|
|||
ARG base_tag=1.0.3-windows-arm32v7
|
||||
ARG base_registry
|
||||
FROM ${base_registry}/azureiotedge/azureiotedge-module-base:${base_tag}
|
||||
|
||||
ARG EXE_DIR=.
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY $EXE_DIR/ ./
|
||||
|
||||
CMD ["dotnet", "NetworkController.dll"]
|
Загрузка…
Ссылка в новой задаче