def __init__(self, file_path, template_name, thing_name, endpoint): """Initializes the provisioning handler Arguments: file_path {string} -- path to your configuration file """ #Logging logging.basicConfig(level=logging.ERROR) self.logger = logging.getLogger(__name__) #Load configuration settings from config.ini config = Config(file_path) self.config_parameters = config.get_section('SETTINGS') self.secure_cert_path = self.config_parameters['SECURE_CERT_PATH'] self.iot_endpoint = endpoint self.template_name = template_name #self.config_parameters['PRODUCTION_TEMPLATE'] self.rotation_template = self.config_parameters['CERT_ROTATION_TEMPLATE'] self.claim_cert = self.config_parameters['CLAIM_CERT'] self.secure_key = self.config_parameters['SECURE_KEY'] self.root_cert = self.config_parameters['ROOT_CERT'] self.root_cert_path = self.config_parameters['ROOT_CERT_PATH'] self.topic_name = self.config_parameters['TOPIC_NAME'] self.unique_id = thing_name self.primary_MQTTClient = None self.test_MQTTClient = None self.callback_returned = False self.message_payload = {} self.isRotation = False self.payloadhandler = payloadHandler(file_path)
def __init__(self, file_path): """Initializes the provisioning handler Arguments: file_path {string} -- path to your configuration file """ # Logging logging.basicConfig(level=logging.ERROR) self.logger = logging.getLogger(__name__) # Load configuration settings from config.ini config = Config(file_path) self.config_parameters = config.get_section('SETTINGS') self.secure_cert_path = self.config_parameters['SECURE_CERT_PATH'] self.iot_endpoint = self.config_parameters['IOT_ENDPOINT'] self.template_name = self.config_parameters[ 'PROVISIONING_TEMPLATE_NAME'] self.claim_cert = self.certFullPath( self.config_parameters['CLAIM_CERT']) self.secure_key = self.certFullPath( self.config_parameters['SECURE_KEY']) self.root_cert = self.certFullPath(self.config_parameters['ROOT_CERT']) self.machine_config = self.config_parameters['MACHINE_CONFIG_PATH'] self.error = '' with open(self.machine_config) as json_file: data = json.load(json_file) self.serial_num = data['serial_num'] self.model_type = data['model_type'] # ------------------------------------------------------------------------------ # -- PROVISIONING HOOKS EXAMPLE -- # Provisioning Hooks are a powerful feature for fleet provisioning. Most of the # heavy lifting is performed within the cloud lambda. However, you can send # device attributes to be validated by the lambda. An example is show in the line # below (.hasValidAccount could be checked in the cloud against a database). # Alternatively, a serial number, geo-location, or any attribute could be sent. # # -- Note: This attribute is passed up as part of the register_thing method and # will be validated in your lambda's event data. # ------------------------------------------------------------------------------ if not os.path.exists(self.claim_cert): self.error = '### Bootstrap cert non-existent. Official cert may already be in place.' else: self.hasValidAccount = True self.primary_MQTTClient = AWSIoTMQTTClient( "fleet_provisioning_demo") self.primary_MQTTClient.onMessage = self.on_message_callback self.callback_returned = False self.message_payload = {}
def __init__(self, file_path): """Initializes the provisioning handler Arguments: file_path {string} -- path to your configuration file """ #Logging logging.basicConfig(level=logging.ERROR) self.logger = logging.getLogger(__name__) #Load configuration settings from config.ini config = Config(file_path) self.config_parameters = config.get_section('SETTINGS') self.secure_cert_path = self.config_parameters['SECURE_CERT_PATH'] self.iot_endpoint = self.config_parameters['IOT_ENDPOINT'] self.template_name = self.config_parameters[ 'PROVISIONING_TEMPLATE_NAME'] self.claim_cert = self.config_parameters['CLAIM_CERT'] self.secure_key = self.config_parameters['SECURE_KEY'] self.root_cert = self.config_parameters['ROOT_CERT'] self.device_info = { 'certPath': self.secure_cert_path, 'endpoint': self.iot_endpoint, 'rootCert': self.root_cert } # Sample Provisioning Template requests a serial number as a # seed to generate Thing names in IoTCore. Simulating here. self.unique_id = str(int(round(time.time() * 1000))) # ------------------------------------------------------------------------------ # -- PROVISIONING HOOKS EXAMPLE -- # Provisioning Hooks are a powerful feature for fleet provisioning. Most of the # heavy lifting is performed within the cloud lambda. However, you can send # device attributes to be validated by the lambda. An example is show in the line # below (.hasValidAccount could be checked in the cloud against a database). # Alternatively, a serial number, geo-location, or any attribute could be sent. # # -- Note: This attribute is passed up as part of the register_thing method and # will be validated in your lambda's event data. # ------------------------------------------------------------------------------ self.hasValidAccount = False self.primary_MQTTClient = AWSIoTMQTTClient("fleet_provisioning_demo") self.test_MQTTClient = AWSIoTMQTTClient( "fleet_provisioning_demo_full_rights") self.primary_MQTTClient.onMessage = self.on_message_callback self.callback_returned = False self.message_payload = {}
def __init__(self, profile, roleName, policyArn, file_path): #Logging logging.basicConfig(level=logging.ERROR) self.logger = logging.getLogger(__name__) config = Config(file_path) self.config_parameters = config.get_section('SETTINGS') self.secure_cert_path = self.config_parameters['SECURE_CERT_PATH'] self.profile = profile self.session = boto3.Session(profile_name = profile) self.client = self.session.client('iot') #self.boto3.client('sts').get_caller_identity().get('Account') self.iam = IAM(profile) self.roleName = roleName self.policyArn = policyArn super().__init__()
def __init__(self, file_path): super().__init__() logging.basicConfig(level=logging.ERROR) self.logger = logging.getLogger(__name__) #Load configuration settings from config.ini config = Config(file_path) self.config_parameters = config.get_section('SETTINGS') self.topic_name = self.config_parameters['TOPIC_NAME'] self.trips_topic_name = self.config_parameters['TRIP_TOPIC_NAME'] self.dtc_topic_name = self.config_parameters['DTC_TOPIC_NAME'] self.csv_location = self.config_parameters['CSV_LOCATION'] self.payload_location = self.config_parameters['PAYLOAD_LOCATION'] self.trips_payload_location = self.config_parameters[ 'TRIP_PAYLOAD_LOCATION'] self.dtc_payload_location = self.config_parameters[ 'DTC_PAYLOAD_LOCATION']
def main(vin, dtc): #Set Config path CONFIG_PATH = 'config.ini' payloadhandler = payloadHandler(CONFIG_PATH) #c = Cognito(profile) #m = ConnectedMobility(profile, stackname) config = Config(CONFIG_PATH) config_parameters = config.get_section('SETTINGS') ENDPOINT = config_parameters['IOT_ENDPOINT'] CLIENT_ID = vin PATH_TO_CERT = "{}/{}".format(config_parameters['SECURE_CERT_PATH'].format(unique_id=CLIENT_ID), config_parameters['PROD_CERT']) PATH_TO_KEY = "{}/{}".format(config_parameters['SECURE_CERT_PATH'].format(unique_id=CLIENT_ID), config_parameters['PROD_KEY']) PATH_TO_ROOT = "{}/{}".format(config_parameters['ROOT_CERT_PATH'], config_parameters['ROOT_CERT']) event_loop_group = io.EventLoopGroup(1) host_resolver = io.DefaultHostResolver(event_loop_group) client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) test_MQTTClient = mqtt_connection_builder.mtls_from_path( endpoint=ENDPOINT, cert_filepath=PATH_TO_CERT, pri_key_filepath=PATH_TO_KEY, client_bootstrap=client_bootstrap, ca_filepath=PATH_TO_ROOT, client_id=CLIENT_ID, clean_session=False, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, keep_alive_secs=6) print("Connecting with Prod certs to {} with client ID '{}'...".format(ENDPOINT, CLIENT_ID)) connect_future = test_MQTTClient.connect() connect_future.result() print("Connected with production certificates to the endpoint") payload = payloadhandler.getDTCPayload( dtc, CLIENT_ID) payloadhandler.publishDTCPayload(test_MQTTClient, payload, CLIENT_ID) print("Successfully published DTC: {}".format(dtc)) exit()
# 7) Using the new certificate, a pub/sub call is demonstrated on a previously forbidden topic to test the new certificate. # 8) New certificates are saved locally, and can be stored/consumed as the application deems necessary. # # # Initial version - Raleigh Murch, AWS # email: [email protected] # ------------------------------------------------------------------------------ from provisioning_handler import ProvisioningHandler from utils.config_loader import Config from pyfiglet import Figlet #Set Config path CONFIG_PATH = 'config.ini' config = Config(CONFIG_PATH) config_parameters = config.get_section('SETTINGS') secure_cert_path = config_parameters['SECURE_CERT_PATH'] bootstrap_cert = config_parameters['CLAIM_CERT'] # Demo Theater f = Figlet(font='slant') print(f.renderText(' F l e e t')) print(f.renderText('Provisioning')) print(f.renderText('----------')) # Provided callback for provisioning method feedback. def callback(payload): print(payload)
def main(profile, vin): #Set Config path CONFIG_PATH = 'config.ini' payloadhandler = payloadHandler(CONFIG_PATH) #c = Cognito(profile) #m = ConnectedMobility(profile, stackname) config = Config(CONFIG_PATH) config_parameters = config.get_section('SETTINGS') #ENDPOINT = config_parameters['IOT_ENDPOINT'] i = IOT(profile, "", "", CONFIG_PATH) ENDPOINT = i.iotEndpoint CLIENT_ID = vin PATH_TO_CERT = "{}/{}".format( config_parameters['SECURE_CERT_PATH'].format(unique_id=CLIENT_ID), config_parameters['PROD_CERT']) PATH_TO_KEY = "{}/{}".format( config_parameters['SECURE_CERT_PATH'].format(unique_id=CLIENT_ID), config_parameters['PROD_KEY']) PATH_TO_ROOT = "{}/{}".format(config_parameters['ROOT_CERT_PATH'], config_parameters['ROOT_CERT']) event_loop_group = io.EventLoopGroup(1) host_resolver = io.DefaultHostResolver(event_loop_group) client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) test_MQTTClient = mqtt_connection_builder.mtls_from_path( endpoint=ENDPOINT, cert_filepath=PATH_TO_CERT, pri_key_filepath=PATH_TO_KEY, client_bootstrap=client_bootstrap, ca_filepath=PATH_TO_ROOT, client_id=CLIENT_ID, clean_session=False, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, keep_alive_secs=6) print("Connecting with Prod certs to {} with client ID '{}'...".format( ENDPOINT, CLIENT_ID)) connect_future = test_MQTTClient.connect() connect_future.result() print("Connected with production certificates to the endpoint") tripId = uuid.uuid4().hex print("Generating Trip ID of {}".format(tripId)) latLongDict = payloadhandler.generateLatLongFromCSV() print("Begin publishing trip data. Will publish {} payloads".format( len(latLongDict))) startCoords = next(iter(latLongDict)) endCoords = list(latLongDict)[-1] startTime = payloadhandler.getTimestampMS() for i in latLongDict: payload = payloadhandler.getPayload(i, tripId, CLIENT_ID) payloadhandler.publishPayload(test_MQTTClient, payload, CLIENT_ID) print("Successfully published coordinates {} of {}".format( i, len(latLongDict))) time.sleep(1) trippayload = payloadhandler.getTripPayload(startTime, startCoords, endCoords, tripId, CLIENT_ID) payloadhandler.publishTripPayload(test_MQTTClient, trippayload, CLIENT_ID) print("Trip data published sucessfully") exit()
class ConnectedMobility(): #external constants cdfStackName = 'CDF' cmsStackName = 'CMS' authStackName = 'Authorizer' assetLibraryUrl = 'AssetLibraryApiGatewayUrl' facadeApiGatewayUrl = 'FacadeApiGatewayUrl' cfUuserPoolClientId = 'UserPoolClientId' cfCloudfrontDomain = 'CloudfrontDomain' cfUserPoolId = 'UserPoolId' cfCertficateId = 'CertificateId' CONFIG_PATH = 'config.ini' config = Config(CONFIG_PATH) config_parameters = config.get_section('SETTINGS') cAcceptHeader = config_parameters['ACCEPT_HEADER'] cContentType = config_parameters['CONTENT_TYPE'] #CF Constants cflogResourceId = 'LogicalResourceId' cfphyResourceId = 'PhysicalResourceId' cfStackResources = 'StackResources' cfStacks = 'Stacks' cfOutputs = 'Outputs' cfOutputKey = 'OutputKey' cfOutputValue = 'OutputValue' __cloudFrontDomainUrl = '' __assetLibraryBaseUrl = '' __cmscfResourcesArn = '' __cognitoId = '' __userPoolClientId = '' __userPoolId = '' __facadeEndpointUrl = '' __certificateId = '' __cdf_stackARN = '' __cdf_cf = {} __cdf_outputs = {} __cdfSubstackOutputs = {} __cf = None session = None def __init__(self, profile, stackname, cdfstackname): self.profile = profile self.stackname = stackname #in a prior version, we could derive the CDF stack name from the resources of the CMS, well that has changed, so now we need to pass in the CDF stack name to get asset library URL self.cdfstackname = cdfstackname self.session = boto3.Session(profile_name = profile) super().__init__() @property def cf(self): if self.__cf == None: self.__cf = self.session.client('cloudformation') return self.__cf @property def cdf_cf(self): if not self.__cdf_cf: self.__cdf_cf = self.cf.describe_stacks(StackName=self.stackname)[self.cfStacks] return self.__cdf_cf @property def cdfOutputs(self): if not self.__cdf_outputs: self.__cdf_outputs = self.cdf_cf[0][self.cfOutputs] return self.__cdf_outputs # @property # def cdfStackARN(self): # if not self.__cdf_stackARN: # self.__cdf_stackARN = self.getValuefromResourceDict(self.cf.describe_stack_resources(StackName=self.stackname)[self.cfStackResources], self.cdfStackName) # return self.__cdf_stackARN # @property @property def cdfSubstackOutputs(self): if not self.__cdfSubstackOutputs: self.__cdfSubstackOutputs = self.cf.describe_stacks(StackName=self.cdfstackname)[self.cfStacks][0][self.cfOutputs] return self.__cdfSubstackOutputs @property def assetLibraryBaseUrl(self): if self.__assetLibraryBaseUrl == '': self.__assetLibraryBaseUrl = self.getValuefromDict(self.cdfSubstackOutputs, self.assetLibraryUrl) return self.__assetLibraryBaseUrl @property def facadeEndpointUrl(self): if self.__facadeEndpointUrl == '': self.__facadeEndpointUrl = self.getValuefromDict(self.cdfOutputs, self.facadeApiGatewayUrl) return self.__facadeEndpointUrl @property def cloudFrontDomainUrl(self): if self.__cloudFrontDomainUrl == '': self.__cloudFrontDomainUrl = self.getValuefromDict(self.cdfOutputs, self.cfCloudfrontDomain) return self.__cloudFrontDomainUrl @property def certificateId(self): if self.__certificateId == '': self.__certificateId = self.getValuefromDict(self.cdfOutputs, self.cfCertficateId) return self.__certificateId def cognitoId(self): return self.__cognitoId @property def userPoolClientId(self): if not self.__userPoolClientId: self.__userPoolClientId = self.getValuefromDict(self.cdfOutputs, self.cfUuserPoolClientId) return self.__userPoolClientId @property def userPoolId(self): if self.__userPoolId == '': self.__userPoolId = self.getValuefromDict(self.cdfOutputs, self.cfUserPoolId) return self.__userPoolId def getValuefromDict(self, vals, keyValName): v = next(filter(lambda output: output[self.cfOutputKey] == keyValName, vals)) return (v[self.cfOutputValue]) def getValuefromResourceDict(self, vals, keyValName): v = next(filter(lambda output: output[self.cflogResourceId] == keyValName, vals)) return (v[self.cfphyResourceId]) def getSupplier(self, deviceMaker, authorization) -> None: #TODO://create function to general the path url = self.assetLibraryBaseUrl + "/groups/%2Fauto%2Fsuppliers%2F{0}" url = url.format(deviceMaker) payload = {} # payload.format(deviceMaker, deviceMaker, GUID) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("GET", url, headers=headers, data=payload) return response def createSupplier(self, deviceMaker, GUID, authorization) -> None: url = self.assetLibraryBaseUrl + "/groups" payload='{{\n \"groupPath\" : \"/auto/suppliers/{0}\",\n \"parentPath\" : \"/auto/suppliers\",\n \"templateId\" : \"auto_supplier\",\n \"name\" : \"{1}\",\n \"attributes\" : {{\n \"externalId\": \"{2}\"\n }}\n}}' payload = payload.format(deviceMaker, deviceMaker, GUID) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("POST", url, headers=headers, data=payload) return response def getCMSUser(self, firstName, lastName, username, authorization) -> None: url = self.facadeEndpointUrl + "/users" payload="{{\n \"username\" : \"{0}\",\n \"firstName\" : \"{1}\",\n \"lastName\" : \"{2}\"\n}}" payload = payload.format(firstName, lastName, username) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("POST", url, headers=headers, data=payload) return response def createCMSUser(self, firstName, lastName, username, authorization) -> None: url = self.facadeEndpointUrl + "/users" payload="{{\n \"username\" : \"{0}\",\n \"firstName\" : \"{1}\",\n \"lastName\" : \"{2}\"\n}}" payload = payload.format(username, firstName, lastName) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("POST", url, headers=headers, data=payload) return response def registerDevice(self, externalId, thingName, authorization) -> None: url = "{0}/suppliers/{1}/devices/{2}/register" url = url.format(self.facadeEndpointUrl, externalId, thingName) payload="{{\n \"templateId\":\"auto_ecu\",\n \"certificateId\": \"{0}\",\n \"attributes\": {{\n \"type\":\"tcu\",\n \"model\":\"TCU-1\"\n }}\n}}" payload = payload.format(self.certificateId) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("POST", url, headers=headers, data=payload) return response def activateDevice(self, deviceMaker, externalId, thingName, vin, authorization) -> None: url = "{0}/suppliers/{1}/devices/{2}/activate" url = url.format(self.facadeEndpointUrl, externalId, thingName) payload = payload='{{\n \"vehicle\": {{\n \"make\": \"{0}\",\n \"model\": \"DN\",\n \"modelYear\": 2019,\n \"marketCode\": \"NA\",\n \"vin\": \"{1}\",\n \"bodyType\": \"Saloon\",\n \"fuelType\": \"Gas\",\n \"transmissionType\": \"Auto\",\n \"transmissionAutoType\": \"7-speed\",\n \"colorCode\": \"B1B!\",\n \"iviType\": \"Premium\",\n \"ecus\": [{{\n \"type\": \"tcu\",\n \"id\": \"{2}\",\n \"softwareVersion\": \"1.9.1\"\n }}]\n }}\n}}' payload = payload.format(deviceMaker, vin, thingName) headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("POST", url, headers=headers, data=payload) return response def associateDevice(self, username, vin, authorization) -> None: url = "{0}/groups/{1}{2}/owns/groups/{3}{4}" url = url.format(self.assetLibraryBaseUrl,"%2Fauto%2Fusers%2F", username, '%2Fauto%2Fvehicles%2F', vin) payload={} headers = { 'Authorization': authorization, 'Accept': self.cAcceptHeader, 'Content-Type': self.cContentType } response = requests.request("PUT", url, headers=headers, data=payload) return response
def main(profile, stackname, cdfstackname, vin, firstname, lastname, username, password): #Set Config path CONFIG_PATH = 'config.ini' config = Config(CONFIG_PATH) config_parameters = config.get_section('SETTINGS') secure_cert_path = config_parameters['SECURE_CERT_PATH'] bootstrap_cert = config_parameters['CLAIM_CERT'] root_cert_url = config_parameters['AWS_ROOT_CERT_URL'] root_cert = config_parameters['ROOT_CERT'] root_cert_path = config_parameters['ROOT_CERT_PATH'] default_role_name = config_parameters['DEFAULT_ROLE_NAME'] default_role_arn = config_parameters['POLICY_ARN_IOT'] deviceMaker = config_parameters['DEVICE_MAKER'] provisioning_template_name = config_parameters['PROVISIONING_TEMPLATE_NAME'] provisioning_template_description = config_parameters['PROVISIONING_TEMPLATE_DESCRIPTION'] provisioning_policy_file_name = config_parameters['POLICY_JSON'] provisioning_template_file_name = config_parameters['TEMPLATE_JSON'] provisioning_policy_name = config_parameters['PROVISIONING_POLICY_NAME'] externalId = uuid.uuid4().hex thingName = vin c = Cognito(profile) m = ConnectedMobility(profile, stackname, cdfstackname) i = IOT(profile, default_role_name, default_role_arn, CONFIG_PATH) provisioner = ProvisioningHandler(CONFIG_PATH, provisioning_template_name, thingName, i.iotEndpoint) if not c.checkCognitoUser(username,m.userPoolId): print("Creating user ...") c.createCognitoUser(username, password, m.userPoolId, m.userPoolClientId) print("Logging in user ...") authorization = c.authenticateCognitoUser(username, password, m.userPoolId, m.userPoolClientId) #check to see if supplier exists, otherwise create a new one. response = m.getSupplier(deviceMaker, authorization) if response.status_code != 200: print("Creating supplier ...") response = m.createSupplier(deviceMaker = deviceMaker, GUID = externalId, authorization = authorization) if response.status_code == 204: print("Vehicle Supplier created successfully...") else: print("Error creating vehicle supplier. Exiting. Error: %s", response) exit() else: #get externalId from existing supplier data = json.loads(response.text) externalId = data["attributes"]["externalId"] print("Creating CMS User ...") response = m.createCMSUser(firstName = firstname, lastName = lastname, username = username, authorization = authorization) if response.status_code == 201 or response.status_code == 204: print("CMS User created successfully...") else: print("Error creating CMS User. May currently exist, who knows, there's no method to check.") print("Registering Device ...") response = m.registerDevice(externalId, thingName, authorization) if response.status_code == 200 or response.status_code == 204 or response.status_code == 201: print("Device registered successfully...") elif response.status_code == 409: print("Device already registered. Will attempt to activate the device...") else: print(response) print("Error registering the device. Exiting.") exit() response = m.activateDevice(deviceMaker = deviceMaker, externalId = externalId, thingName = thingName, vin = vin, authorization = authorization) if response.status_code == 204: print("Device activated successfully...") else: print("Error activating device. Exiting.") exit() response = m.associateDevice(username = username, vin = vin, authorization = authorization) if response.status_code == 204 or response.status_code == 200: print("Device associated successfully...") print("External ID: {}".format(externalId)) print("Thing Name: {}".format(thingName)) else: print("Error associating device to user. Exiting.") exit() #begin setting up device certificates for this thing #We will use fleet provisioning to take a bootstrap certificate, this bootstrap certificate is allowed to connect to specific topics that will allow for the creation #of the permananet certificate. The permanent certificate is then downloaded to the /certs folder and used to connect to the telemetry topics print("Begin setting up provisioning templates and certificates...") v = i.setupProvisioningTemplate( provisioning_template_name, provisioning_template_description, provisioning_template_file_name, provisioning_policy_name, provisioning_policy_file_name, vin) if v == True: try: #to get root cert if it does not exist print("Check that the provisioning template has been created") template = i.describeProvisioningTemplate(provisioning_template_name) print(template) print("Getting root certificate") root_path = "{}/{}".format( root_cert_path, root_cert) if not os.path.exists( root_path): response = urlopen(root_cert_url) content = response.read().decode('utf-8') with open(root_path, "w" ) as f: f.write( content ) f.close() except Exception as e: print(e) exit() print("Root certificate downloaded to certificates directory.") try: print("{}/{}".format(secure_cert_path.format(unique_id=vin), bootstrap_cert)) with open("{}/{}".format(secure_cert_path.format(unique_id=vin), bootstrap_cert), 'r') as f: # Call super-method to perform aquisition/activation # of certs, association of thing, etc. Returns general # purpose callback at this point. # Instantiate provisioning handler, pass in path to config provisioner.get_official_certs(callback) except IOError: print("### Bootstrap cert non-existent. Official cert may already be in place.") print("Vehicle setup sucessfully, please visit http://{} to login with your user and see your vehicle".format(m.cloudFrontDomainUrl))