Collect IoT Hub hostname hash and suffix when deploying (#328)

* Collect IoT Hub hostname hash and suffix when deploying

* Handle empty hostname

* Move hash connection method to connection string class

* Calculate hostname hash when init connection string

* Refactor ConnectionString class
This commit is contained in:
Ray Fang 2018-10-17 09:48:03 +08:00 коммит произвёл GitHub
Родитель ce79408f68
Коммит f71356fb34
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 101 добавлений и 56 удалений

Просмотреть файл

@ -3,16 +3,14 @@ import os
import signal
import subprocess
import sys
try:
from queue import Queue, Empty
except ImportError:
from Queue import Queue, Empty # python 2.x
from io import StringIO
from threading import Timer, Thread
from threading import Thread, Timer
from azure.cli.core import get_default_cli
from fstrings import f
from six.moves.queue import Empty, Queue
from . import telemetry
output_io_cls = StringIO
@ -357,11 +355,14 @@ class AzureCli:
return result
def set_modules(self, device_id, connection_string, hub_name, config):
def set_modules(self, device_id, connection_string, config):
self.output.status(f("Deploying '{config}' to '{device_id}'..."))
telemetry.add_extra_props({'iothubhostname': connection_string.iothub_host.name_hash, 'iothubhostnamesuffix': connection_string.iothub_host.name_suffix})
config = os.path.join(os.getcwd(), config)
return self.invoke_az_cli_outproc(["iot", "edge", "set-modules", "-d", device_id, "-n", hub_name, "-k", config, "-l", connection_string],
return self.invoke_az_cli_outproc(["iot", "edge", "set-modules", "-d", device_id, "-n", connection_string.iothub_host.hub_name, "-k", config, "-l", connection_string.connection_string],
error_message=f("Failed to deploy '{config}' to '{device_id}'..."), suppress_output=True)
def monitor_events(self, device_id, connection_string, hub_name, timeout=300):

Просмотреть файл

@ -1,10 +1,13 @@
from .utility import Utility
class ConnectionString:
def __init__(self, value):
self.ConnectionString = value
self.connection_string = value
self.data = dict()
if self.ConnectionString:
parts = self.ConnectionString.split(';')
if self.connection_string:
parts = self.connection_string.split(';')
if len(parts) > 0:
for part in parts:
subpart = part.split('=', 1)
@ -12,10 +15,8 @@ class ConnectionString:
self.data[subpart[0].lower()] = subpart[1].strip()
if self.data:
self.HostName = self["hostname"]
if self.HostName:
self.HubName = self.HostName.split('.')[0]
self.SharedAccessKey = self["sharedaccesskey"]
self.iothub_host = IoTHubHost(self["hostname"])
self.shared_access_key = self["sharedaccesskey"]
def __getitem__(self, key):
return self.data[key]
@ -25,13 +26,28 @@ class IoTHubConnectionString(ConnectionString):
def __init__(self, value):
ConnectionString.__init__(self, value)
if self.ConnectionString:
self.SharedAccessKeyName = self["sharedaccesskeyname"]
if self.connection_string:
self.shared_access_key_name = self["sharedaccesskeyname"]
class DeviceConnectionString(ConnectionString):
def __init__(self, value):
ConnectionString.__init__(self, value)
if self.ConnectionString:
self.DeviceId = self["deviceid"]
if self.connection_string:
self.device_id = self["deviceid"]
class IoTHubHost:
def __init__(self, hostname):
self.name = hostname
if self.name and "." in self.name:
self.hub_name = self.name.split('.')[0]
# get connection string hostname hash to count distint IoT Hub number
self.name_hash = Utility.get_sha256_hash(self.name)
# get hostname suffix (e.g., azure-devices.net) to distinguish national clouds
self.name_suffix = self.name[self.name.index(".")+1:]
else:
self.hub_name = ""
self.name_hash = ""
self.name_suffix = ""

Просмотреть файл

@ -1,4 +1,3 @@
import hashlib
import sys
from functools import wraps
@ -73,8 +72,10 @@ def hash256_result(func):
raise ValueError('Return value is None')
elif not isinstance(val, six.string_types):
raise ValueError('Return value is not string')
hash_object = hashlib.sha256(val.encode('utf-8'))
return str(hash_object.hexdigest())
from .utility import Utility
return Utility.get_sha256_hash(val)
return _wrapped_func

Просмотреть файл

@ -12,7 +12,6 @@ class Edge:
self.envvars.verify_envvar_has_val("DEVICE_CONNECTION_STRING", self.envvars.DEVICE_CONNECTION_INFO)
self.envvars.verify_envvar_has_val("DEPLOYMENT_CONFIG_FILE", self.envvars.DEPLOYMENT_CONFIG_FILE)
self.azure_cli.set_modules(self.envvars.DEVICE_CONNECTION_INFO.DeviceId, self.envvars.IOTHUB_CONNECTION_INFO.ConnectionString,
self.envvars.IOTHUB_CONNECTION_INFO.HubName, self.envvars.DEPLOYMENT_CONFIG_FILE_PATH)
self.azure_cli.set_modules(self.envvars.DEVICE_CONNECTION_INFO.device_id, self.envvars.IOTHUB_CONNECTION_INFO, self.envvars.DEPLOYMENT_CONFIG_FILE_PATH)
self.output.footer("DEPLOYMENT COMPLETE")

Просмотреть файл

@ -31,18 +31,18 @@ class IoTHub:
if timeout == 0:
self.utility.call_proc(['iothub-explorer', '--login', self.envvars.IOTHUB_CONNECTION_STRING,
'monitor-events', self.envvars.DEVICE_CONNECTION_INFO.DeviceId], shell=not self.envvars.is_posix())
'monitor-events', self.envvars.DEVICE_CONNECTION_INFO.device_id], shell=not self.envvars.is_posix())
else:
monitor_js = os.path.join(os.path.split(__file__)[0], "monitor.js")
self.utility.call_proc(['node', monitor_js, self.envvars.IOTHUB_CONNECTION_STRING,
self.envvars.DEVICE_CONNECTION_INFO.DeviceId, timeout], shell=not self.envvars.is_posix())
self.envvars.DEVICE_CONNECTION_INFO.device_id, timeout], shell=not self.envvars.is_posix())
except Exception as ex:
self.output.error("Problem while trying to call iothub-explorer. Please ensure that you have installed the iothub-explorer npm package with: npm i -g iothub-explorer.")
self.output.error(str(ex))
def monitor_events_cli(self, timeout=0):
self.azure_cli.monitor_events(self.envvars.DEVICE_CONNECTION_INFO.DeviceId,
self.envvars.IOTHUB_CONNECTION_INFO.ConnectionString,
self.envvars.IOTHUB_CONNECTION_INFO.HubName,
self.azure_cli.monitor_events(self.envvars.DEVICE_CONNECTION_INFO.device_id,
self.envvars.IOTHUB_CONNECTION_INFO.connection_string,
self.envvars.IOTHUB_CONNECTION_INFO.iothub_host.hub_name,
timeout)

Просмотреть файл

@ -51,6 +51,8 @@ class TelemetrySession(object):
if self.exception:
props['Exception'] = self.exception
props.update(self.extra_props)
self.events[_get_AI_key()].append({
'name': '{}/command'.format(PRODUCT_NAME),
'properties': props
@ -106,6 +108,12 @@ def fail(exception, summary):
_session.result_summary = summary
@suppress_all_exceptions()
def add_extra_props(props):
if props is not None:
_session.extra_props.update(props)
@_user_agrees_to_telemetry
@suppress_all_exceptions()
def flush():

Просмотреть файл

@ -188,3 +188,9 @@ class Utility:
current = current.get(key)
current[keys[-1]] = value
@staticmethod
def get_sha256_hash(val):
hash_object = sha256(val.encode('utf-8'))
return str(hash_object.hexdigest()).lower()

Просмотреть файл

@ -8,11 +8,12 @@ pytestmark = pytest.mark.unit
emptystring = ""
valid_connectionstring = "HostName=testhub.azure-devices.net;SharedAccessKey=gibberish"
valid_iothub_connectionstring = "HostName=testhub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=moregibberish"
valid_iothub_connectionstring = "HostName=ChaoyiTestIoT.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=moregibberish"
valid_device_connectionstring = "HostName=testhub.azure-devices.net;DeviceId=testdevice;SharedAccessKey=othergibberish"
invalid_connectionstring = "HostName=azure-devices.net;SharedAccessKey=gibberish"
invalid_iothub_connectionstring = "HostName=testhub.azure-devices.net;SharedAccessKey=moregibberish"
invalid_device_connectionstring = "HostName=testhub.azure-devices.net;DeviceId=;SharedAccessKey=othergibberish"
empty_hostname_iothub_connectionstring = "HostName=;SharedAccessKeyName=iothubowner;SharedAccessKey=moregibberish"
def test_empty_connectionstring():
@ -20,6 +21,14 @@ def test_empty_connectionstring():
assert not connectionstring.data
def test_empty_hostname_iothub_connectionstring():
connectionstring = ConnectionString(empty_hostname_iothub_connectionstring)
assert connectionstring.iothub_host.name == ""
assert connectionstring.iothub_host.hub_name == ""
assert connectionstring.shared_access_key == "moregibberish"
assert connectionstring.iothub_host.name_hash == ""
def test_empty_iothub_connectionstring():
connectionstring = IoTHubConnectionString(emptystring)
assert not connectionstring.data
@ -32,30 +41,31 @@ def test_empty_device_connectionstring():
def test_valid_connectionstring():
connectionstring = ConnectionString(valid_connectionstring)
assert connectionstring.HostName == "testhub.azure-devices.net"
assert connectionstring.HubName == "testhub"
assert connectionstring.SharedAccessKey == "gibberish"
assert connectionstring.iothub_host.name == "testhub.azure-devices.net"
assert connectionstring.iothub_host.hub_name == "testhub"
assert connectionstring.shared_access_key == "gibberish"
def test_valid_iothub_connectionstring():
connectionstring = IoTHubConnectionString(valid_iothub_connectionstring)
assert connectionstring.HostName == "testhub.azure-devices.net"
assert connectionstring.HubName == "testhub"
assert connectionstring.SharedAccessKeyName == "iothubowner"
assert connectionstring.SharedAccessKey == "moregibberish"
assert connectionstring.iothub_host.name == "ChaoyiTestIoT.azure-devices.net"
assert connectionstring.iothub_host.hub_name == "ChaoyiTestIoT"
assert connectionstring.shared_access_key_name == "iothubowner"
assert connectionstring.shared_access_key == "moregibberish"
assert connectionstring.iothub_host.name_hash == "6b8fcfea09003d5f104771e83bd9ff54c592ec2277ec1815df91dd64d1633778"
def test_valid_devicehub_connectionstring():
connectionstring = DeviceConnectionString(valid_device_connectionstring)
assert connectionstring.HostName == "testhub.azure-devices.net"
assert connectionstring.HubName == "testhub"
assert connectionstring.DeviceId == "testdevice"
assert connectionstring.SharedAccessKey == "othergibberish"
assert connectionstring.iothub_host.name == "testhub.azure-devices.net"
assert connectionstring.iothub_host.hub_name == "testhub"
assert connectionstring.device_id == "testdevice"
assert connectionstring.shared_access_key == "othergibberish"
def test_invalid_connectionstring():
connectionstring = ConnectionString(invalid_connectionstring)
assert connectionstring.HubName != "testhub"
assert connectionstring.iothub_host.hub_name != "testhub"
def test_invalid_iothub_connectionstring():
@ -65,7 +75,7 @@ def test_invalid_iothub_connectionstring():
def test_invalid_devicehub_connectionstring():
connectionstring = DeviceConnectionString(invalid_device_connectionstring)
assert connectionstring.HostName == "testhub.azure-devices.net"
assert connectionstring.HubName == "testhub"
assert not connectionstring.DeviceId
assert connectionstring.SharedAccessKey == "othergibberish"
assert connectionstring.iothub_host.name == "testhub.azure-devices.net"
assert connectionstring.iothub_host.hub_name == "testhub"
assert not connectionstring.device_id
assert connectionstring.shared_access_key == "othergibberish"

Просмотреть файл

@ -210,20 +210,20 @@ def test_valid_env_iothub_connectionstring():
env_iothub_connectionstring = os.getenv("IOTHUB_CONNECTION_STRING")
connectionstring = IoTHubConnectionString(env_iothub_connectionstring)
assert connectionstring.HostName
assert connectionstring.HubName
assert connectionstring.SharedAccessKey
assert connectionstring.SharedAccessKeyName
assert connectionstring.iothub_host.name
assert connectionstring.iothub_host.hub_name
assert connectionstring.shared_access_key
assert connectionstring.shared_access_key_name
def test_valid_env_device_connectionstring():
envvars.load_dotenv()
env_device_connectionstring = os.getenv("DEVICE_CONNECTION_STRING")
connectionstring = DeviceConnectionString(env_device_connectionstring)
assert connectionstring.HostName
assert connectionstring.HubName
assert connectionstring.SharedAccessKey
assert connectionstring.DeviceId
assert connectionstring.iothub_host.name
assert connectionstring.iothub_host.hub_name
assert connectionstring.shared_access_key
assert connectionstring.device_id
def test_shared_lib():

Просмотреть файл

@ -58,7 +58,7 @@ def test_start_single():
cli = __import__('iotedgedev.cli', fromlist=['main'])
runner = CliRunner()
result = runner.invoke(cli.main, ['simulator', 'start', '-i', 'setup'])
result = runner.invoke(cli.main, ['simulator', 'start', '-i', 'input1'])
print(result.output)
assert 'IoT Edge Simulator has been started in single module mode.' in result.output

Просмотреть файл

@ -81,3 +81,7 @@ def test_in_asterisk_list_empty(utility):
def test_in_asterisk_list_asterisk(utility):
assert utility.in_asterisk_list("filtermodule", "*")
def test_get_sha256_hash():
assert Utility.get_sha256_hash("foo") == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"