public async Task ExportDevicesAsync(string hubConnectionString,
                                             string blobContainerUri,
                                             string userDefinedManagedIdentityResourceId = null)
        {
            using RegistryManager srcRegistryManager = RegistryManager.CreateFromConnectionString(hubConnectionString);

            // If StorageAuthenticationType is set to IdentityBased and userAssignedIdentity property is
            // not null, the jobs will use user defined managed identity. If the IoT hub is not
            // configured with the user defined managed identity specified in userAssignedIdentity,
            // the job will fail.
            // If StorageAuthenticationType is set to IdentityBased and userAssignedIdentity property is
            // null, the jobs will use system defined identity by default. If the IoT hub is configured with the
            // system defined managed identity, the job will succeed but will not use the user defined managed identity.
            // If the IoT hub is not configured with system defined managed identity, the job will fail.
            // If StorageAuthenticationType is set to IdentityBased and neither user defined nor system defined
            // managed identities are configured on the hub, the job will fail.
            JobProperties jobProperties = JobProperties.CreateForExportJob(
                outputBlobContainerUri: blobContainerUri,
                excludeKeysInExport: false,
                storageAuthenticationType: StorageAuthenticationType.IdentityBased,
                identity: new ManagedIdentity
            {
                userAssignedIdentity = userDefinedManagedIdentityResourceId
            });

            JobProperties jobResult = await srcRegistryManager
                                      .ExportDevicesAsync(jobProperties);

            // Poll every 5 seconds to see if the job has finished executing.
            while (true)
            {
                jobResult = await srcRegistryManager.GetJobAsync(jobResult.JobId);

                if (jobResult.Status == JobStatus.Completed)
                {
                    break;
                }
                else if (jobResult.Status == JobStatus.Failed)
                {
                    throw new Exception("Export job failed.");
                }
                else if (jobResult.Status == JobStatus.Cancelled)
                {
                    throw new Exception("Export job was canceled.");
                }
                else
                {
                    await Task.Delay(TimeSpan.FromSeconds(5));
                }
            }
        }
Пример #2
0
        public async Task RegistryManager_ExportDevices(StorageAuthenticationType storageAuthenticationType)
        {
            // arrange

            StorageContainer storageContainer = null;
            string           deviceId         = $"{nameof(RegistryManager_ExportDevices)}-{StorageContainer.GetRandomSuffix(4)}";
            var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString);

            _log.WriteLine($"Using deviceId {deviceId}");

            try
            {
                string containerName = StorageContainer.BuildContainerName(nameof(RegistryManager_ExportDevices));
                storageContainer = await StorageContainer
                                   .GetInstanceAsync(containerName)
                                   .ConfigureAwait(false);

                _log.WriteLine($"Using container {storageContainer.Uri}");

                Uri containerUri = storageAuthenticationType == StorageAuthenticationType.KeyBased
                    ? storageContainer.SasUri
                    : storageContainer.Uri;

                await registryManager
                .AddDeviceAsync(
                    new Device(deviceId)
                {
                    Authentication = new AuthenticationMechanism {
                        Type = AuthenticationType.Sas
                    },
                })
                .ConfigureAwait(false);

                // act

                JobProperties exportJobResponse = null;
                int           tryCount          = 0;
                while (true)
                {
                    try
                    {
                        exportJobResponse = await registryManager
                                            .ExportDevicesAsync(
                            JobProperties.CreateForExportJob(
                                containerUri.ToString(),
                                true,
                                null,
                                storageAuthenticationType))
                                            .ConfigureAwait(false);

                        break;
                    }
                    // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests
                    catch (JobQuotaExceededException) when(++tryCount < MaxIterationWait)
                    {
                        _log.WriteLine($"JobQuotaExceededException... waiting.");
                        await Task.Delay(s_waitDuration).ConfigureAwait(false);

                        continue;
                    }
                }

                // wait for job to complete
                for (int i = 0; i < MaxIterationWait; ++i)
                {
                    await Task.Delay(s_waitDuration).ConfigureAwait(false);

                    exportJobResponse = await registryManager.GetJobAsync(exportJobResponse.JobId).ConfigureAwait(false);

                    _log.WriteLine($"Job {exportJobResponse.JobId} is {exportJobResponse.Status} with progress {exportJobResponse.Progress}%");
                    if (!s_incompleteJobs.Contains(exportJobResponse.Status))
                    {
                        break;
                    }
                }

                // assert

                exportJobResponse.Status.Should().Be(JobStatus.Completed, "Otherwise import failed");
                exportJobResponse.FailureReason.Should().BeNullOrEmpty("Otherwise import failed");

                string content = await DownloadFileAsync(storageContainer).ConfigureAwait(false);

                string[] serializedDevices = content.Split(s_newlines, StringSplitOptions.RemoveEmptyEntries);

                bool foundDeviceInExport = false;
                foreach (string serializedDeivce in serializedDevices)
                {
                    // The first line may be a comment to the user, so skip any lines that don't start with a json object initial character: curly brace
                    if (serializedDeivce[0] != '{')
                    {
                        continue;
                    }

                    ExportImportDevice device = JsonConvert.DeserializeObject <ExportImportDevice>(serializedDeivce);
                    if (StringComparer.Ordinal.Equals(device.Id, deviceId))
                    {
                        _log.WriteLine($"Found device in export as [{serializedDeivce}]");
                        foundDeviceInExport = true;
                        break;
                    }
                }
                foundDeviceInExport.Should().BeTrue("Expected device did not appear in the export");
            }
            finally
            {
                try
                {
                    storageContainer?.Dispose();

                    await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false);
                }
                catch { }
            }
        }
Пример #3
0
        private async Task CleanupDevices()
        {
            Console.WriteLine($"Using storage container {_blobContainerClient.Name}" +
                              $" for exporting devices identities to and importing device identities from.");

            // Retrieve the SAS Uri that will be used to grant access to the storage containers.
            string storageAccountSasUri = GetStorageAccountSasUriForCleanupJob(_blobContainerClient).ToString();

            // Step 1: Export all device identities.
            JobProperties exportAllDevicesProperties = JobProperties
                                                       .CreateForExportJob(
                outputBlobContainerUri: storageAccountSasUri,
                excludeKeysInExport: true,
                storageAuthenticationType: StorageAuthenticationType.KeyBased);

            JobProperties exportAllDevicesJob = null;

            int tryCount = 0;

            while (true)
            {
                try
                {
                    exportAllDevicesJob = await _registryManager.ExportDevicesAsync(exportAllDevicesProperties);

                    break;
                }
                // Wait for pending jobs to finish.
                catch (JobQuotaExceededException) when(++tryCount < MaxIterationWait)
                {
                    Console.WriteLine($"JobQuotaExceededException... waiting.");
                    await Task.Delay(WaitDuration);
                }
            }

            if (exportAllDevicesJob == null)
            {
                throw new Exception("Export devices job failed.");
            }

            // Wait until the export job is finished.
            while (true)
            {
                exportAllDevicesJob = await _registryManager.GetJobAsync(exportAllDevicesJob.JobId);

                if (s_completedJobs.Contains(exportAllDevicesJob.Status))
                {
                    // Job has finished executing.
                    break;
                }

                Console.WriteLine($"Job {exportAllDevicesJob.JobId} is {exportAllDevicesJob.Status} with progress {exportAllDevicesJob.Progress}%");
                await Task.Delay(s_waitDuration);
            }
            Console.WriteLine($"Job {exportAllDevicesJob.JobId} is {exportAllDevicesJob.Status}.");

            if (exportAllDevicesJob.Status != JobStatus.Completed)
            {
                throw new Exception("Exporting devices failed, exiting.");
            }

            // Step 2: Download the exported devices list from the blob create in Step 1.
            BlobClient       blobClient = _blobContainerClient.GetBlobClient(ImportExportDevicesFileName);
            BlobDownloadInfo download   = await blobClient.DownloadAsync();

            IEnumerable <ExportImportDevice> exportedDevices = ImportExportDevicesHelpers.BuildExportImportDeviceFromStream(download.Content);

            // Step 3: Collect the devices that need to be deleted and update their ImportMode to be Delete.
            // Thie step will create an ExportImportDevice identity for each device/ module identity registered on hub.
            // If you hub instance has IoT Hub module or Edge module instances registered, then they will be counted as separate entities
            // from the corresponding IoT Hub device/ Edge device that they are associated with.
            // As a result, the count of ExportImportDevice identities to be deleted might be greater than the
            // count of IoT hub devices retrieved in PrintDeviceCountAsync().
            var devicesToBeDeleted = new List <ExportImportDevice>();

            foreach (var device in exportedDevices)
            {
                string deviceId = device.Id;
                foreach (string prefix in _deleteDevicesWithPrefix)
                {
                    if (deviceId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                    {
                        devicesToBeDeleted.Add(device);
                    }
                }
            }
            devicesToBeDeleted
            .ForEach(device => device.ImportMode = ImportMode.Delete);
            Console.WriteLine($"Retrieved {devicesToBeDeleted.Count} devices for deletion.");

            if (devicesToBeDeleted.Count > 0)
            {
                // Step 3a: Write the new import data back to the blob.
                using Stream devicesFile = ImportExportDevicesHelpers.BuildDevicesStream(devicesToBeDeleted);
                await blobClient.UploadAsync(devicesFile, overwrite : true);

                // Step 3b: Call import using the same blob to delete all devices.
                JobProperties importDevicesToBeDeletedProperties = JobProperties
                                                                   .CreateForImportJob(
                    inputBlobContainerUri: storageAccountSasUri,
                    outputBlobContainerUri: storageAccountSasUri,
                    storageAuthenticationType: StorageAuthenticationType.KeyBased);

                JobProperties importDevicesToBeDeletedJob = null;

                tryCount = 0;
                while (true)
                {
                    try
                    {
                        importDevicesToBeDeletedJob = await _registryManager.ImportDevicesAsync(importDevicesToBeDeletedProperties);

                        break;
                    }
                    // Wait for pending jobs to finish.
                    catch (JobQuotaExceededException) when(++tryCount < MaxIterationWait)
                    {
                        Console.WriteLine($"JobQuotaExceededException... waiting.");
                        await Task.Delay(WaitDuration);
                    }
                }

                if (importDevicesToBeDeletedJob == null)
                {
                    throw new Exception("Import devices job failed.");
                }

                // Wait until job is finished.
                while (true)
                {
                    importDevicesToBeDeletedJob = await _registryManager.GetJobAsync(importDevicesToBeDeletedJob.JobId);

                    if (s_completedJobs.Contains(importDevicesToBeDeletedJob.Status))
                    {
                        // Job has finished executing.
                        break;
                    }

                    Console.WriteLine($"Job {importDevicesToBeDeletedJob.JobId} is {importDevicesToBeDeletedJob.Status} with progress {importDevicesToBeDeletedJob.Progress}%");
                    await Task.Delay(s_waitDuration);
                }
                Console.WriteLine($"Job {importDevicesToBeDeletedJob.JobId} is {importDevicesToBeDeletedJob.Status}.");
            }

            // Step 4: Delete the storage container created.
            await _blobContainerClient.DeleteAsync();

            Console.WriteLine($"Storage container {_blobContainerClient.Name} deleted.");
        }
        private async Task <JobProperties> CreateAndWaitForJobAsync(
            StorageAuthenticationType storageAuthenticationType,
            bool isUserAssignedMsi,
            string devicesFileName,
            string configsFileName,
            RegistryManager registryManager,
            Uri containerUri)
        {
            int tryCount = 0;

            ManagedIdentity identity = isUserAssignedMsi
                ? new ManagedIdentity
            {
                UserAssignedIdentity = TestConfiguration.IoTHub.UserAssignedMsiResourceId
            }
                : null;

            JobProperties exportJobResponse = JobProperties.CreateForExportJob(
                containerUri.ToString(),
                true,
                devicesFileName,
                storageAuthenticationType,
                identity);

            exportJobResponse.IncludeConfigurations  = true;
            exportJobResponse.ConfigurationsBlobName = configsFileName;

            while (tryCount < MaxIterationWait)
            {
                try
                {
                    exportJobResponse = await registryManager.ExportDevicesAsync(exportJobResponse).ConfigureAwait(false);

                    break;
                }
                // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests
                catch (JobQuotaExceededException) when(++tryCount < MaxIterationWait)
                {
                    Logger.Trace($"JobQuotaExceededException... waiting.");
                    await Task.Delay(s_waitDuration).ConfigureAwait(false);

                    continue;
                }
            }

            for (int i = 0; i < MaxIterationWait; ++i)
            {
                await Task.Delay(s_waitDuration).ConfigureAwait(false);

                exportJobResponse = await registryManager.GetJobAsync(exportJobResponse.JobId).ConfigureAwait(false);

                Logger.Trace($"Job {exportJobResponse.JobId} is {exportJobResponse.Status} with progress {exportJobResponse.Progress}%");
                if (!s_incompleteJobs.Contains(exportJobResponse.Status))
                {
                    break;
                }
            }

            exportJobResponse.Status.Should().Be(JobStatus.Completed, "Otherwise import failed");
            exportJobResponse.FailureReason.Should().BeNullOrEmpty("Otherwise import failed");

            return(exportJobResponse);
        }