コード例 #1
0
            /// <summary>
            /// Equivalent to GetSetOfUniqueNodesAsync
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="endpointId">Id of the endpoint as returned by <see cref="Registry_GetEndpoints(IIoTPlatformTestContext)"/></param>
            /// <param name="nodeId">Id of the parent node or null to browse the root node</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task <List <(string NodeId, string NodeClass, bool Children)> > GetBrowseEndpointAsync(
                IIoTPlatformTestContext context,
                string endpointId,
                string nodeId        = null,
                CancellationToken ct = default)
            {
                if (string.IsNullOrEmpty(endpointId))
                {
                    context.OutputHelper.WriteLine($"{nameof(endpointId)} is null or empty");
                    throw new ArgumentNullException(nameof(endpointId));
                }

                var    result            = new List <(string NodeId, string NodeClass, bool Children)>();
                string continuationToken = null;

                do
                {
                    var browseResult = await GetBrowseEndpoint_InternalAsync(context, endpointId, nodeId, continuationToken, ct);

                    if (browseResult.results.Count > 0)
                    {
                        result.AddRange(browseResult.results);
                    }

                    continuationToken = browseResult.continuationToken;
                } while (continuationToken != null);

                return(result);
            }
コード例 #2
0
        /// <summary>
        /// Delete a file on the Edge VM
        /// </summary>
        /// <param name="fileName">Filename of the file to delete</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        public static void DeleteFileOnEdgeVM(string fileName, IIoTPlatformTestContext context)
        {
            var isSuccessful = false;

            using var client = CreateSshClientAndConnect(context);

            var terminal = client.RunCommand("rm " + fileName);

            if (string.IsNullOrEmpty(terminal.Error) || terminal.Error.ToLowerInvariant().Contains("no such file"))
            {
                isSuccessful = true;
            }
            Assert.True(isSuccessful, "Delete file was not successful");

            if (context.IoTEdgeConfig.NestedEdgeFlag == "Enable")
            {
                using var sshCient = CreateSshClientAndConnect(context);
                foreach (var edge in context.IoTEdgeConfig.NestedEdgeSshConnections)
                {
                    if (edge != string.Empty)
                    {
                        var command = $"ssh -oStrictHostKeyChecking=no {edge} 'sudo rm {fileName}'";
                        sshCient.RunCommand(command);
                    }
                }
            }
        }
コード例 #3
0
        /// <summary>
        /// Gets endpoints from registry
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        private static async Task <dynamic> GetEndpointInternalAsync(IIoTPlatformTestContext context, CancellationToken ct)
        {
            var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

            var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
            {
                Timeout = TestConstants.DefaultTimeoutInMilliseconds
            };

            var request = new RestRequest(Method.GET);

            request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
            request.Resource = TestConstants.APIRoutes.RegistryEndpoints;

            var response = await client.ExecuteAsync(request, ct).ConfigureAwait(false);

            Assert.NotNull(response);

            if (!response.IsSuccessful)
            {
                context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                Assert.True(response.IsSuccessful, "GET /registry/v2/endpoints failed!");
            }

            return(JsonConvert.DeserializeObject <ExpandoObject>(response.Content, new ExpandoObjectConverter()));
        }
コード例 #4
0
            /// <summary>
            /// Unregisters a server identified by <paramref name="applicationId"/>
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="discoveryUrl">Discovery URL the application is registered to</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task UnregisterServerAsync(
                IIoTPlatformTestContext context,
                string discoveryUrl,
                CancellationToken ct = default)
            {
                var applicationId = await GetApplicationIdAsync(context, discoveryUrl, ct);

                var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                var request = new RestRequest(Method.DELETE);

                request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                request.Resource = string.Format(TestConstants.APIRoutes.RegistryApplicationsWithApplicationIdFormat, applicationId);

                var response = await client.ExecuteAsync(request, ct).ConfigureAwait(false);

                Assert.NotNull(response);

                if (!response.IsSuccessful)
                {
                    context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                    context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    Assert.True(response.IsSuccessful, "DELETE /registry/v2/application/{applicationId} failed!");
                }
            }
コード例 #5
0
        /// <summary>
        /// Stops the monitoring of incoming event to an IoT Hub and returns success/failure.
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        /// <returns></returns>
        public static async Task <dynamic> StopMonitoringIncomingMessagesAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            // TODO Merge with Start-Method to avoid code duplication
            var runtimeUrl = context.TestEventProcessorConfig.TestEventProcessorBaseUrl.TrimEnd('/') + "/Runtime";

            var client = new RestClient(runtimeUrl)
            {
                Timeout       = TestConstants.DefaultTimeoutInMilliseconds,
                Authenticator = new HttpBasicAuthenticator(context.TestEventProcessorConfig.TestEventProcessorUsername,
                                                           context.TestEventProcessorConfig.TestEventProcessorPassword)
            };

            var body = new {
                CommandType = CommandEnum.Stop,
            };

            var request = new RestRequest(Method.PUT);

            request.AddJsonBody(body);

            var response = await client.ExecuteAsync(request, ct);

            dynamic json = JsonConvert.DeserializeObject(response.Content);

            Assert.NotNull(json);
            Assert.NotEmpty(json);

            return(json);
        }
コード例 #6
0
        /// <summary>
        /// transfer the content of published_nodes.json file into the OPC Publisher edge module
        /// </summary>
        /// <param name="entries">Entries for published_nodes.json</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        public static async Task PublishNodesAsync(
            IIoTPlatformTestContext context,
            string publishedNodesFullPath,
            IEnumerable <PublishedNodesEntryModel> entries
            )
        {
            var json = JsonConvert.SerializeObject(entries, Formatting.Indented);

            context.OutputHelper?.WriteLine("Write published_nodes.json to IoT Edge");
            context.OutputHelper?.WriteLine(json);
            CreateFolderOnEdgeVM(TestConstants.PublishedNodesFolder, context);
            using var scpClient    = CreateScpClientAndConnect(context);
            await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));

            scpClient.Upload(stream, publishedNodesFullPath);

            if (context.IoTEdgeConfig.NestedEdgeFlag == "Enable")
            {
                using var sshCient = CreateSshClientAndConnect(context);
                foreach (var edge in context.IoTEdgeConfig.NestedEdgeSshConnections)
                {
                    if (edge != string.Empty)
                    {
                        // Copy file to the edge vm
                        var command = $"scp -oStrictHostKeyChecking=no {publishedNodesFullPath} {edge}:{TestConstants.PublishedNodesFilename}";
                        sshCient.RunCommand(command);

                        // Move file to the target folder with sudo permissions
                        command = $"ssh -oStrictHostKeyChecking=no {edge} 'sudo mv {TestConstants.PublishedNodesFilename} {publishedNodesFullPath}'";
                        sshCient.RunCommand(command);
                    }
                }
            }
        }
コード例 #7
0
            /// <summary>
            /// Wait until the OPC UA endpoint is detected
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="ct">Cancellation token</param>
            /// <param name="requestedEndpointUrls">List of OPC UA endpoint URLS that need to be activated and connected</param>
            /// <returns>content of GET /registry/v2/endpoints request as dynamic object</returns>
            public static async Task <dynamic> WaitForEndpointDiscoveryToBeCompleted(
                IIoTPlatformTestContext context,
                CancellationToken ct = default,
                IEnumerable <string> requestedEndpointUrls = null)
            {
                ct.ThrowIfCancellationRequested();

                try {
                    dynamic json;
                    int     foundEndpoints = 0;
                    int     numberOfItems;
                    bool    shouldExit = false;
                    do
                    {
                        json = await GetEndpointInternalAsync(context, ct).ConfigureAwait(false);

                        foundEndpoints = 0;

                        Assert.NotNull(json);
                        numberOfItems = (int)json.items.Count;
                        if (numberOfItems <= 0)
                        {
                            await Task.Delay(TestConstants.DefaultDelayMilliseconds);
                        }
                        else
                        {
                            for (int indexOfOpcUaEndpoint = 0; indexOfOpcUaEndpoint < numberOfItems; indexOfOpcUaEndpoint++)
                            {
                                var endpoint = ((string)json.items[indexOfOpcUaEndpoint].registration.endpoint.url).TrimEnd('/');

                                if (requestedEndpointUrls == null || requestedEndpointUrls.Contains(endpoint))
                                {
                                    foundEndpoints++;
                                }
                            }

                            var expectedNumberOfEndpoints = requestedEndpointUrls != null
                                                            ? requestedEndpointUrls.Count()
                                                            : 1;

                            if (foundEndpoints < expectedNumberOfEndpoints)
                            {
                                await Task.Delay(TestConstants.DefaultDelayMilliseconds);
                            }
                            else
                            {
                                shouldExit = true;
                            }
                        }
                    } while (!shouldExit);

                    return(json);
                }
                catch (Exception e) {
                    context.OutputHelper?.WriteLine("Error: OPC UA endpoint not found in time");
                    PrettyPrintException(e, context.OutputHelper);
                    throw;
                }
            }
コード例 #8
0
        /// <summary>
        /// Wait for all API services of IIoT platform to be healthy.
        /// </summary>
        /// <param name="context"> Shared Context for E2E testing Industrial IoT Platform </param>
        /// <param name="ct"> Cancellation token </param>
        /// <returns></returns>
        public static async Task WaitForServicesAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            const string healthyState = "Healthy";

            var healthRoutes = new string[] {
                TestConstants.APIRoutes.RegistryHealth,
                TestConstants.APIRoutes.PublisherHealth,
                TestConstants.APIRoutes.TwinHealth,
                TestConstants.APIRoutes.JobOrchestratorHealth
            };

            try {
                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                while (true)
                {
                    ct.ThrowIfCancellationRequested();

                    var tasks = new List <Task <IRestResponse> >();

                    foreach (var healthRoute in healthRoutes)
                    {
                        var request = new RestRequest(Method.GET)
                        {
                            Resource = healthRoute
                        };

                        tasks.Add(client.ExecuteAsync(request, ct));
                    }

                    Task.WaitAll(tasks.ToArray());

                    var healthyServices = tasks
                                          .Where(task => task.Result.StatusCode == HttpStatusCode.OK)
                                          .Count(task => task.Result.Content == healthyState);

                    if (healthyServices == healthRoutes.Length)
                    {
                        context.OutputHelper?.WriteLine("All API microservices of IIoT platform " +
                                                        "are running and in healthy state.");
                        return;
                    }

                    await Task.Delay(TestConstants.DefaultDelayMilliseconds, ct);
                }
            }
            catch (Exception) {
                context.OutputHelper?.WriteLine("Error: not all API microservices of IIoT " +
                                                "platform are in healthy state.");
                throw;
            }
        }
コード例 #9
0
            /// <summary>
            /// Gets the application ID associated with the DiscoveryUrl property of <paramref name="context"/>
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="discoveryUrl">Discovery URL the application is registered to</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task <string> GetApplicationIdAsync(
                IIoTPlatformTestContext context,
                string discoveryUrl,
                CancellationToken ct = default)
            {
                var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                var request = new RestRequest(Method.GET);

                request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                request.Resource = TestConstants.APIRoutes.RegistryApplications;

                var response = await client.ExecuteAsync(request, ct).ConfigureAwait(false);

                Assert.NotNull(response);

                if (!response.IsSuccessful)
                {
                    context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                    context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    Assert.True(response.IsSuccessful, "GET /registry/v2/application failed!");
                }

                dynamic result = JsonConvert.DeserializeObject <ExpandoObject>(response.Content, new ExpandoObjectConverter());
                var     json   = (IDictionary <string, object>)result;

                Assert.True(HasProperty(result, "items"), "GET /registry/v2/application response did not contain items");
                Assert.False(result.items == null, "GET /registry/v2/application response items property is null");

                foreach (var item in result.items)
                {
                    var itemDictionary = (IDictionary <string, object>)item;

                    if (!itemDictionary.ContainsKey("discoveryUrls") ||
                        !itemDictionary.ContainsKey("applicationId"))
                    {
                        continue;
                    }

                    var discoveryUrls = (List <object>)item.discoveryUrls;
                    var itemUrl       = (string)discoveryUrls?.FirstOrDefault(url => IsUrlStringsEqual(url as string, discoveryUrl));

                    if (itemUrl != null)
                    {
                        return(item.applicationId);
                    }
                }

                return(null);
            }
コード例 #10
0
        /// <summary>
        /// Gets the private SSH key from the configuration to connect to the Edge VM
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <returns></returns>
        private static PrivateKeyFile GetPrivateSshKey(IIoTPlatformTestContext context)
        {
            context.OutputHelper?.WriteLine("Load private key from environment variable");

            var buffer           = Encoding.Default.GetBytes(context.SshConfig.PrivateKey);
            var privateKeyStream = new MemoryStream(buffer);

            var privateKeyFile = new PrivateKeyFile(privateKeyStream);

            return(privateKeyFile);
        }
コード例 #11
0
        /// <summary>
        /// Update Device Twin tag
        /// </summary>
        /// <param name="patch">Name of deployed Industrial IoT</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        public static async Task UpdateTagAsync(
            string patch,
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            var registryManager = context.RegistryHelper.RegistryManager;
            var twin            = await registryManager.GetTwinAsync(context.DeviceConfig.DeviceId, ct);

            await registryManager.UpdateTwinAsync(twin.DeviceId, patch, twin.ETag, ct);
        }
コード例 #12
0
ファイル: TestHelper.cs プロジェクト: buzzfrog/Industrial-IoT
        /// <summary>
        /// Sets the unmanaged-Tag to "true" to enable Standalone-Mode
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <returns></returns>
        private static async Task SwitchToStandaloneModeAsync(IIoTPlatformTestContext context, CancellationToken ct = default)
        {
            var patch =
                @"{
                    tags: {
                        unmanaged: true
                    }
                }";

            await UpdateTagAsync(patch, context, ct);
        }
コード例 #13
0
            /// <summary>
            /// Activates (and waits for activated and connected state) the endpoint from <paramref name="context"/>
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task ActivateEndpointAsync(IIoTPlatformTestContext context, string endpointId, CancellationToken ct = default)
            {
                var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

                Assert.False(string.IsNullOrWhiteSpace(endpointId), "Endpoint not set in the test context");

                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                var request = new RestRequest(Method.POST);

                request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                request.Resource = string.Format(TestConstants.APIRoutes.RegistryActivateEndpointsFormat, endpointId);

                // TODO remove workaround
                // This request used to fail when called for the first time. The bug was fixed in the master,
                // but we will keep this workaround until we can consume the fixed platform in the E2E tests.
                bool IsSuccessful    = false;
                int  numberOfRetries = 3;

                while (!IsSuccessful && numberOfRetries > 0)
                {
                    var response = await client.ExecuteAsync(request, ct);

                    IsSuccessful = response.IsSuccessful;
                    numberOfRetries--;
                }

                Assert.True(IsSuccessful, "POST /registry/v2/endpoints/{endpointId}/activate failed!");

                while (true)
                {
                    Assert.False(ct.IsCancellationRequested, "Endpoint was not activated within the expected timeout");

                    var endpointList = await GetEndpointsAsync(context, ct).ConfigureAwait(false);

                    var endpoint = endpointList.FirstOrDefault(e => string.Equals(e.Id, endpointId));

                    if (string.Equals(endpoint.ActivationState, TestConstants.StateConstants.ActivatedAndConnected) &&
                        string.Equals(endpoint.EndpointState, TestConstants.StateConstants.Ready))
                    {
                        return;
                    }

                    context.OutputHelper?.WriteLine(string.IsNullOrEmpty(endpoint.Url) ? "Endpoint not found" :
                                                    $"Endpoint state: {endpoint.EndpointState}, activation: {endpoint.ActivationState}");

                    await Task.Delay(TestConstants.DefaultDelayMilliseconds).ConfigureAwait(false);
                }
            }
コード例 #14
0
        /// <summary>
        /// Read PublishedNodes json from OPC-PLC and provide the data to the tests
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        /// <returns>Dictionary with URL of PLC-PLC as key and Content of Published Nodes files as value</returns>
        public static async Task <IDictionary <string, PublishedNodesEntryModel> > GetSimulatedPublishedNodesConfigurationAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            var result = new Dictionary <string, PublishedNodesEntryModel>();

            var opcPlcList = context.OpcPlcConfig.Urls;

            context.OutputHelper?.WriteLine($"SimulatedOpcPlcUrls {opcPlcList}");
            var ipAddressList = opcPlcList.Split(TestConstants.SimulationUrlsSeparator);

            foreach (var ipAddress in ipAddressList.Where(s => !string.IsNullOrWhiteSpace(s)))
            {
                try {
                    using (var client = new HttpClient()) {
                        var ub = new UriBuilder {
                            Host = ipAddress
                        };
                        var baseAddress = ub.Uri;

                        client.BaseAddress = baseAddress;
                        client.Timeout     = TimeSpan.FromMilliseconds(TestConstants.DefaultTimeoutInMilliseconds);

                        using (var response = await client.GetAsync(TestConstants.OpcSimulation.PublishedNodesFile, ct)) {
                            Assert.NotNull(response);
                            Assert.True(response.IsSuccessStatusCode, $"http GET request to load pn.json failed, Status {response.StatusCode}");
                            var json = await response.Content.ReadAsStringAsync();

                            Assert.NotEmpty(json);
                            var entryModels = JsonConvert.DeserializeObject <PublishedNodesEntryModel[]>(json);

                            Assert.NotNull(entryModels);
                            Assert.NotEmpty(entryModels);
                            Assert.NotNull(entryModels[0].OpcNodes);
                            Assert.NotEmpty(entryModels[0].OpcNodes);

                            // Set endpoint url correctly when it's not specified in pn.json ie. replace fqdn with the ip address
                            string fqdn = Regex.Match(entryModels[0].EndpointUrl, @"opc.tcp:\/\/([^\}]+):").Groups[1].Value;
                            entryModels[0].EndpointUrl = entryModels[0].EndpointUrl.Replace(fqdn, ipAddress);

                            result.Add(ipAddress, entryModels[0]);
                        }
                    }
                }
                catch (Exception e) {
                    context.OutputHelper?.WriteLine("Error occurred while downloading Message: {0} skipped: {1}", e.Message, ipAddress);
                    continue;
                }
            }
            return(result);
        }
コード例 #15
0
 /// <summary>
 /// Request OAuth token using Http basic authentication from environment variables
 /// </summary>
 /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
 /// <param name="ct">Cancellation token</param>
 /// <returns>Return content of request token or empty string</returns>
 public static async Task <string> GetTokenAsync(
     IIoTPlatformTestContext context,
     CancellationToken ct = default
     )
 {
     return(await GetTokenAsync(
                context.IIoTPlatformConfigHubConfig.AuthTenant,
                context.IIoTPlatformConfigHubConfig.AuthClientId,
                context.IIoTPlatformConfigHubConfig.AuthClientSecret,
                context.IIoTPlatformConfigHubConfig.ApplicationName,
                ct
                ));
 }
コード例 #16
0
ファイル: TestHelper.cs プロジェクト: buzzfrog/Industrial-IoT
        /// <summary>
        /// Sets the unmanaged-Tag to "true" to enable Orchestrated-Mode
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <returns></returns>
        public static async Task SwitchToOrchestratedModeAsync(IIoTPlatformTestContext context, CancellationToken ct = default)
        {
            var patch =
                @"{
                    tags: {
                        unmanaged: null
                    }
                }";

            await UpdateTagAsync(patch, context, ct);

            DeleteFileOnEdgeVM(TestConstants.PublishedNodesFullName, context);
        }
コード例 #17
0
        /// <summary>
        /// Wait for first OPC UA endpoint to be activated
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        /// <returns>content of GET /registry/v2/endpoints request as dynamic object</returns>
        public static async Task <dynamic> WaitForEndpointToBeActivatedAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default)
        {
            var accessToken = await TestHelper.GetTokenAsync(context, ct);

            var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
            {
                Timeout = TestConstants.DefaultTimeoutInMilliseconds
            };

            ct.ThrowIfCancellationRequested();
            try {
                dynamic json;
                string  activationState;
                do
                {
                    var request = new RestRequest(Method.GET);
                    request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                    request.Resource = TestConstants.APIRoutes.RegistryEndpoints;

                    var response = await client.ExecuteAsync(request, ct);

                    Assert.NotNull(response);
                    Assert.True(response.IsSuccessful, "GET /registry/v2/endpoints failed!");

                    if (!response.IsSuccessful)
                    {
                        context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                        context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    }

                    Assert.NotEmpty(response.Content);
                    json = JsonConvert.DeserializeObject(response.Content);
                    Assert.NotNull(json);

                    activationState = (string)json.items[0].activationState;
                    // wait the endpoint to be connected
                    if (activationState == "Activated")
                    {
                        await Task.Delay(TestConstants.DefaultTimeoutInMilliseconds, ct);
                    }
                } while (activationState != "ActivatedAndConnected");

                return(json);
            }
            catch (Exception) {
                context.OutputHelper?.WriteLine("Error: OPC UA endpoint couldn't be activated");
                throw;
            }
        }
コード例 #18
0
        /// <summary>
        /// Delete a file on the Edge VM
        /// </summary>
        /// <param name="fileName">Filename of the file to delete</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        public static void DeleteFileOnEdgeVM(string fileName, IIoTPlatformTestContext context)
        {
            var isSuccessful = false;

            using var client = CreateSshClientAndConnect(context);

            var terminal = client.RunCommand("rm " + fileName);

            if (string.IsNullOrEmpty(terminal.Error) || terminal.Error.ToLowerInvariant().Contains("no such file"))
            {
                isSuccessful = true;
            }
            Assert.True(isSuccessful, "Delete file was not successful");
        }
コード例 #19
0
        /// <summary>
        /// Read PublishedNodes json from OPC-PLC and provide the data to the tests
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        /// <returns>Dictionary with URL of PLC-PLC as key and Content of Published Nodes files as value</returns>
        public static async Task <IDictionary <string, PublishedNodesEntryModel> > GetSimulatedPublishedNodesConfigurationAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            var result = new Dictionary <string, PublishedNodesEntryModel>();

            var opcPlcUrls = context.OpcPlcConfig.Urls;

            context.OutputHelper?.WriteLine($"SimulatedOpcPlcUrls {opcPlcUrls}");
            var listOfUrls = opcPlcUrls.Split(TestConstants.SimulationUrlsSeparator);

            foreach (var url in listOfUrls.Where(s => !string.IsNullOrWhiteSpace(s)))
            {
                context.OutputHelper?.WriteLine($"Load pn.json from {url}");
                try {
                    using (var client = new HttpClient()) {
                        var ub = new UriBuilder {
                            Host = url
                        };
                        var baseAddress = ub.Uri;

                        client.BaseAddress = baseAddress;
                        client.Timeout     = TimeSpan.FromMilliseconds(TestConstants.DefaultTimeoutInMilliseconds);

                        using (var response = await client.GetAsync(TestConstants.OpcSimulation.PublishedNodesFile, ct)) {
                            Assert.NotNull(response);
                            Assert.True(response.IsSuccessStatusCode, $"http GET request to load pn.json failed, Status {response.StatusCode}");
                            var json = await response.Content.ReadAsStringAsync();

                            Assert.NotEmpty(json);
                            var entryModels = JsonConvert.DeserializeObject <PublishedNodesEntryModel[]>(json);

                            Assert.NotNull(entryModels);
                            Assert.NotEmpty(entryModels);
                            Assert.NotNull(entryModels[0].OpcNodes);
                            Assert.NotEmpty(entryModels[0].OpcNodes);

                            result.Add(url, entryModels[0]);
                        }
                    }
                }
                catch (Exception e) {
                    context.OutputHelper?.WriteLine("Error occurred while downloading Message: {0} skipped: {1}", e.Message, url);
                    continue;
                }
            }
            return(result);
        }
コード例 #20
0
            /// <summary>
            /// Activates (and waits for activated and connected state) the endpoint from <paramref name="context"/>
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task ActivateEndpointAsync(IIoTPlatformTestContext context, string endpointId, CancellationToken ct = default)
            {
                var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

                Assert.False(string.IsNullOrWhiteSpace(endpointId), "Endpoint not set in the test context");

                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                var request = new RestRequest(Method.POST);

                request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                request.Resource = string.Format(TestConstants.APIRoutes.RegistryActivateEndpointsFormat, endpointId);

                var response = client.ExecuteAsync(request, ct).GetAwaiter().GetResult();

                Assert.NotNull(response);

                if (!response.IsSuccessful)
                {
                    context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                    context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    Assert.True(response.IsSuccessful, "POST /registry/v2/endpoints/{endpointId}/activate failed!");
                }

                Assert.Empty(response.Content);

                while (true)
                {
                    Assert.False(ct.IsCancellationRequested, "Endpoint was not activated within the expected timeout");

                    var endpointList = await GetEndpointsAsync(context, ct).ConfigureAwait(false);

                    var endpoint = endpointList.FirstOrDefault(e => string.Equals(e.Id, endpointId));

                    if (string.Equals(endpoint.ActivationState, TestConstants.StateConstants.ActivatedAndConnected) &&
                        string.Equals(endpoint.EndpointState, TestConstants.StateConstants.Ready))
                    {
                        return;
                    }

                    context.OutputHelper?.WriteLine(string.IsNullOrEmpty(endpoint.Url) ? "Endpoint not found" :
                                                    $"Endpoint state: {endpoint.EndpointState}, activation: {endpoint.ActivationState}");

                    await Task.Delay(TestConstants.DefaultDelayMilliseconds).ConfigureAwait(false);
                }
            }
コード例 #21
0
        /// <summary>
        /// Wait until the OPC UA server is discovered
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        /// <returns>content of GET /registry/v2/application request as dynamic object</returns>
        public static async Task <dynamic> WaitForDiscoveryToBeCompletedAsync(
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            ct.ThrowIfCancellationRequested();

            try {
                dynamic json;
                int     numberOfItems;
                do
                {
                    var accessToken = await TestHelper.GetTokenAsync(context, ct);

                    var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                    {
                        Timeout = TestConstants.DefaultTimeoutInMilliseconds
                    };

                    var request = new RestRequest(Method.GET);
                    request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                    request.Resource = TestConstants.APIRoutes.RegistryApplications;

                    var response = await client.ExecuteAsync(request, ct);

                    Assert.NotNull(response);
                    Assert.True(response.IsSuccessful, "GET /registry/v2/application failed!");

                    if (!response.IsSuccessful)
                    {
                        context.OutputHelper?.WriteLine($"StatusCode: {response.StatusCode}");
                        context.OutputHelper?.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    }

                    Assert.NotEmpty(response.Content);
                    json = JsonConvert.DeserializeObject(response.Content);
                    Assert.NotNull(json);
                    numberOfItems = (int)json.items.Count;
                } while (numberOfItems <= 0);

                return(json);
            }
            catch (Exception) {
                context.OutputHelper?.WriteLine("Error: discovery module didn't find OPC UA server in time");
                throw;
            }
        }
コード例 #22
0
        /// <summary>
        /// Create a folder on Edge VM (if not exists)
        /// </summary>
        /// <param name="folderPath">Name of the folder to create.</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        private static void CreateFolderOnEdgeVM(string folderPath, IIoTPlatformTestContext context)
        {
            Assert.True(!string.IsNullOrWhiteSpace(folderPath), "folder does not exist");

            var isSuccessful = false;

            using var client = CreateSshClientAndConnect(context);

            var terminal = client.RunCommand("sudo mkdir " + folderPath + ";" + "cd " + folderPath + "; " + "sudo chmod 777 " + folderPath);

            if (string.IsNullOrEmpty(terminal.Error) || terminal.Error.Contains("File exists"))
            {
                isSuccessful = true;
            }

            Assert.True(isSuccessful, "Folder creation was not successful");
        }
コード例 #23
0
        /// <summary>
        /// Starts monitoring the incoming messages of the IoT Hub and checks for missing values.
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="expectedValuesChangesPerTimestamp">The expected number of value changes per timestamp</param>
        /// <param name="expectedIntervalOfValueChanges">The expected time difference between values changes in milliseconds</param>
        /// <param name="expectedMaximalDuration">The time difference between OPC UA Server fires event until Changes Received in IoT Hub in milliseconds </param>
        /// <param name="ct">Cancellation token</param>
        /// <returns></returns>
        public static async Task StartMonitoringIncomingMessagesAsync(
            IIoTPlatformTestContext context,
            int expectedValuesChangesPerTimestamp,
            int expectedIntervalOfValueChanges,
            int expectedMaximalDuration,
            CancellationToken ct = default
            )
        {
            var runtimeUrl = context.TestEventProcessorConfig.TestEventProcessorBaseUrl.TrimEnd('/') + "/Runtime";

            var client = new RestClient(runtimeUrl)
            {
                Authenticator = new HttpBasicAuthenticator(context.TestEventProcessorConfig.TestEventProcessorUsername,
                                                           context.TestEventProcessorConfig.TestEventProcessorPassword),
            };

            var body = new {
                CommandType   = CommandEnum.Start,
                Configuration = new {
                    IoTHubEventHubEndpointConnectionString = context.IoTHubConfig.IoTHubEventHubConnectionString,
                    StorageConnectionString          = context.IoTHubConfig.CheckpointStorageConnectionString,
                    ExpectedValueChangesPerTimestamp = expectedValuesChangesPerTimestamp,
                    ExpectedIntervalOfValueChanges   = expectedIntervalOfValueChanges,
                    ThresholdValue = expectedIntervalOfValueChanges > 0
                        ? expectedIntervalOfValueChanges / 10
                        : 100,
                    ExpectedMaximalDuration = expectedMaximalDuration,
                }
            };

            var request = new RestRequest("", Method.Put)
            {
                Timeout = TestConstants.DefaultTimeoutInMilliseconds,
            };

            request.AddJsonBody(body);

            var response = await client.ExecuteAsync(request, ct);

            Assert.True(response.IsSuccessful, $"Response status code: {response.StatusCode}");

            dynamic json = JsonConvert.DeserializeObject(response.Content);

            Assert.NotNull(json);
        }
コード例 #24
0
        /// <summary>
        /// Create a new ScpClient based on SshConfig and directly connects to host
        /// </summary>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <returns>Instance of SshClient, that need to be disposed</returns>
        private static ScpClient CreateScpClientAndConnect(IIoTPlatformTestContext context)
        {
            var privateKeyFile = GetPrivateSshKey(context);

            context.OutputHelper?.WriteLine("Create SCP Client");
            var client = new ScpClient(
                context.SshConfig.Host,
                context.SshConfig.Username,
                privateKeyFile);

            context.OutputHelper?.WriteLine("open scp connection to host {0} with username {1}",
                                            context.SshConfig.Host,
                                            context.SshConfig.Username);
            client.Connect();
            context.OutputHelper?.WriteLine("scp connection successful established");

            return(client);
        }
コード例 #25
0
            /// <summary>
            /// Equivalent to recursive calling GetSetOfUniqueNodesAsync to get the whole hierarchy of nodes
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="endpointId">Id of the endpoint as returned by <see cref="Registry_GetEndpoints(IIoTPlatformTestContext)"/></param>
            /// <param name="nodeClass">Class of the node to filter to or null for no filtering</param>
            /// <param name="nodeId">Id of the parent node or null to browse the root node</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task <List <(string NodeId, string NodeClass, bool Children)> > GetBrowseEndpointRecursiveAsync(
                IIoTPlatformTestContext context,
                string endpointId,
                string nodeClass     = null,
                string nodeId        = null,
                CancellationToken ct = default)
            {
                if (string.IsNullOrEmpty(endpointId))
                {
                    context.OutputHelper.WriteLine($"{nameof(endpointId)} is null or empty");
                    throw new ArgumentNullException(nameof(endpointId));
                }

                var nodes = new ConcurrentBag <(string NodeId, string NodeClass, bool Children)>();

                await GetBrowseEndpointRecursiveCollectResultsAsync(context, endpointId, nodes, nodeId, ct);

                return(nodes.Where(n => string.Equals(nodeClass, n.NodeClass, StringComparison.OrdinalIgnoreCase)).ToList());
            }
コード例 #26
0
        /// <summary>
        /// transfer the content of published_nodes.json file into the OPC Publisher edge module
        /// </summary>
        /// <param name="entries">Entries for published_nodes.json</param>
        /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
        /// <param name="ct">Cancellation token</param>
        public static async Task SwitchToStandaloneModeAndPublishNodesAsync(
            IEnumerable <PublishedNodesEntryModel> entries,
            IIoTPlatformTestContext context,
            CancellationToken ct = default
            )
        {
            DeleteFileOnEdgeVM(TestConstants.PublishedNodesFullName, context);

            var json = JsonConvert.SerializeObject(entries, Formatting.Indented);

            context.OutputHelper?.WriteLine("Write published_nodes.json to IoT Edge");
            context.OutputHelper?.WriteLine(json);
            CreateFolderOnEdgeVM(TestConstants.PublishedNodesFolder, context);
            using var client       = CreateScpClientAndConnect(context);
            await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
            client.Upload(stream, TestConstants.PublishedNodesFullName);

            await SwitchToStandaloneModeAsync(context, ct);
        }
コード例 #27
0
            /// <summary>
            /// Waits for the discovery to be completed and then gets the Endpoint ID
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="requestedEndpointUrl">Endpoint URL to get the ID for</param>
            /// <param name="ct">Cancellation token</param>
            /// <returns></returns>
            public static async Task <string> GetOpcUaEndpointId(
                IIoTPlatformTestContext context,
                string requestedEndpointUrl,
                CancellationToken ct)
            {
                var json = await WaitForEndpointDiscoveryToBeCompleted(context, ct, new List <string> {
                    requestedEndpointUrl
                });

                int numberOfItems = json.items.Count;

                for (var indexOfOpcUaEndpoint = 0; indexOfOpcUaEndpoint < numberOfItems; indexOfOpcUaEndpoint++)
                {
                    var endpoint = ((string)json.items[indexOfOpcUaEndpoint].registration.endpointUrl).TrimEnd('/');
                    if (endpoint == requestedEndpointUrl)
                    {
                        return((string)json.items[indexOfOpcUaEndpoint].registration.id);
                    }
                }

                return(null);
            }
コード例 #28
0
        /// <summary>
        /// Clean published nodes JSON files for both legacy (2.5) and current (2.8) versions.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static async Task CleanPublishedNodesJsonFilesAsync(IIoTPlatformTestContext context)
        {
            // Make sure directories exist.
            using (var sshCient = CreateSshClientAndConnect(context)) {
                sshCient.RunCommand($"[ ! -d { TestConstants.PublishedNodesFolder} ]" +
                                    $" && sudo mkdir -m 777 -p {TestConstants.PublishedNodesFolder}");
                sshCient.RunCommand($"[ ! -d { TestConstants.PublishedNodesFolderLegacy} ]" +
                                    $" && sudo mkdir -m 777 -p {TestConstants.PublishedNodesFolderLegacy}");
            }

            await PublishNodesAsync(
                context,
                TestConstants.PublishedNodesFullName,
                Array.Empty <PublishedNodesEntryModel>()
                ).ConfigureAwait(false);

            await PublishNodesAsync(
                context,
                TestConstants.PublishedNodesFullNameLegacy,
                Array.Empty <PublishedNodesEntryModel>()
                ).ConfigureAwait(false);
        }
コード例 #29
0
            /// <summary>
            /// Registers a server, the discovery url will be saved in the <paramref name="context"/>
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="discoveryUrl">Discovery URL to register</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task RegisterServerAsync(
                IIoTPlatformTestContext context,
                string discoveryUrl,
                CancellationToken ct = default)
            {
                if (string.IsNullOrEmpty(discoveryUrl))
                {
                    context.OutputHelper.WriteLine($"{nameof(discoveryUrl)} is null or empty");
                    throw new ArgumentNullException(nameof(discoveryUrl));
                }

                var accessToken = await GetTokenAsync(context, ct).ConfigureAwait(false);

                var client = new RestClient(context.IIoTPlatformConfigHubConfig.BaseUrl)
                {
                    Timeout = TestConstants.DefaultTimeoutInMilliseconds
                };

                var request = new RestRequest(Method.POST);

                request.AddHeader(TestConstants.HttpHeaderNames.Authorization, accessToken);
                request.Resource = TestConstants.APIRoutes.RegistryApplications;

                var body = new { discoveryUrl };

                request.AddJsonBody(JsonConvert.SerializeObject(body));

                var response = await client.ExecuteAsync(request, ct).ConfigureAwait(false);

                Assert.NotNull(response);

                if (!response.IsSuccessful)
                {
                    context.OutputHelper.WriteLine($"StatusCode: {response.StatusCode}");
                    context.OutputHelper.WriteLine($"ErrorMessage: {response.ErrorMessage}");
                    Assert.True(response.IsSuccessful, "POST /registry/v2/application failed!");
                }
            }
コード例 #30
0
            /// <summary>
            /// Gets endpoints from registry
            /// </summary>
            /// <param name="context">Shared Context for E2E testing Industrial IoT Platform</param>
            /// <param name="ct">Cancellation token</param>
            public static async Task <List <(string Id, string Url, string ActivationState, string EndpointState)> > GetEndpointsAsync(
                IIoTPlatformTestContext context,
                CancellationToken ct = default)
            {
                dynamic json = await GetEndpointInternalAsync(context, ct).ConfigureAwait(false);

                Assert.True(HasProperty(json, "items"), "GET /registry/v2/endpoints response has no items");
                Assert.False(json.items == null, "GET /registry/v2/endpoints response items property is null");
                Assert.NotEqual(0, json.items.Count);

                var result = new List <(string Id, string Url, string ActivationState, string EndpointState)>();

                foreach (var item in json.items)
                {
                    var id              = item.registration.id?.ToString();
                    var endpointUrl     = item.registration.endpointUrl?.ToString();
                    var activationState = item.activationState?.ToString();
                    var endpointState   = item.endpointState?.ToString();
                    result.Add((id, endpointUrl, activationState, endpointState));
                }

                return(result);
            }