def associate_devices(self, thing_names, config_file, region=None): # TODO remove this function when Group discovery is enriched """ Using the `thing_names` values, associate existing Things in AWS IoT with the config of another Greengrass Group for use as Greengrass Devices. :param thing_names: the thing name or list of thing names to associate as Greengrass Devices :param config_file: config file used to track the Greengrass Devices in the group :param region: the region in which to associate devices. [default: us-west-2] """ logging.info("associate_devices thing_names:{0}".format(thing_names)) config = GroupConfigFile(config_file=config_file) if region is None: region = self._region devices = config['devices'] if type(thing_names) is basestring: thing_names = [thing_names] iot_client = _get_iot_session(region=region) for thing_name in thing_names: thing = iot_client.describe_thing(thingName=thing_name) logging.info("Found existing Thing:{0}".format(thing)) p = iot_client.list_thing_principals(thingName=thing_name) logging.info("Existing Thing has principals:{0}".format(p)) devices[thing_name] = { 'thing_arn': thing['attributes']['thingArn'], 'cert_arn': p['principals'][0], 'cert_id': thing['attributes']['certificateId'], 'thing_name': thing_name } logging.info("Thing:'{0}' associated with config:'{1}'".format( thing_name, config_file)) config['devices'] = devices
def core_connect(device_name, config_file, root_ca, certificate, private_key, group_ca_path): global ggd_name, mqttc cfg = GroupConfigFile(config_file) ggd_name = cfg['devices'][device_name]['thing_name'] iot_endpoint = cfg['misc']['iot_endpoint'] dip = DiscoveryInfoProvider() dip.configureEndpoint(iot_endpoint) dip.configureCredentials(caPath=root_ca, certPath=certificate, keyPath=private_key) dip.configureTimeout(10) # 10 sec logging.info("[button] Discovery using CA:{0} cert:{1} prv_key:{2}".format( root_ca, certificate, private_key)) gg_core, discovery_info = utils.discover_configured_core( device_name=device_name, dip=dip, config_file=config_file, ) if not gg_core: raise EnvironmentError("[button] Couldn't find the Core") ca_list = discovery_info.getAllCas() group_id, ca = ca_list[0] group_ca_file = utils.save_group_ca(ca, group_ca_path, group_id) mqttc = AWSIoTMQTTClient(ggd_name) # local Greengrass Core discovered, now connect to Core from this Device log.info("[button] gca_file:{0} cert:{1}".format(group_ca_file, certificate)) mqttc.configureCredentials(group_ca_file, private_key, certificate) mqttc.configureOfflinePublishQueueing(10, DROP_OLDEST) return mqttc, gg_core
help="Topic used to communicate arm telemetry.") parser.add_argument('--frequency', default=1.0, dest='frequency', type=float, help="Modify the default telemetry sample frequency.") parser.add_argument('--debug', default=False, action='store_true', help="Activate debug output.") args = parser.parse_args() if args.debug: log.setLevel(logging.DEBUG) logging.getLogger('servode').setLevel(logging.DEBUG) cfg = GroupConfigFile(args.config_file) ggd_name = cfg['devices']['GGD_arm']['thing_name'] ggd_ca_file_path = args.ca_file_path initialize() with ServoProtocol() as sp: sg = ServoGroup() sg['base'] = Servo(sp, ggd_config.arm_servo_ids[0], base_servo_cache) sg['femur01'] = Servo(sp, ggd_config.arm_servo_ids[1], femur01_servo_cache) sg['femur02'] = Servo(sp, ggd_config.arm_servo_ids[2], femur02_servo_cache) sg['tibia'] = Servo(sp, ggd_config.arm_servo_ids[3], tibia_servo_cache) sg['effector'] = Servo(sp, ggd_config.arm_servo_ids[4], eff_servo_cache)
def create_devices(self, thing_names, config_file, region=None, cert_dir=None, append=False, account_id=None, policy_name='ggd-discovery-policy', profile_name=None): """ Using the `thing_names` values, creates Things in AWS IoT, attaches and downloads new keys & certs to the certificate directory, then records the created information in the local config file for inclusion in the Greengrass Group as Greengrass Devices. :param thing_names: the thing name or list of thing names to create and use as Greengrass Devices :param config_file: config file used to track the Greengrass Devices in the group :param region: the region in which to create the new devices. [default: us-west-2] :param cert_dir: the directory in which to store the thing's keys and certs. If `None` then use the current directory. :param append: append the created devices to the list of devices in the config file. [default: False] :param account_id: the account ID in which to create devices. If 'None' the config_file will be checked for an `account_id` value in the `misc` section. :param policy_name: the name of the policy to associate with the device. [default: 'ggd-discovery-policy'] :param profile_name: the name of the `awscli` profile to use. [default: None] """ logging.info("create_devices thing_names:{0}".format(thing_names)) config = GroupConfigFile(config_file=config_file) if append is False and config.is_device_fresh() is False: raise ValueError( "Config file tracking previously created devices. Append " "devices instead" ) if region is None: region = self._region if account_id is None: account_id = self._account_id devices = dict() if append: devices = config['devices'] if type(thing_names) is str: thing_names = [thing_names] iot_client = _get_iot_session(region=region, profile_name=profile_name) for thing_name in thing_names: keys_cert, thing = self.create_thing(thing_name, region, cert_dir) cert_arn = keys_cert['certificateArn'] devices[thing_name] = { 'thing_arn': thing['thingArn'], 'cert_arn': cert_arn, 'cert_id': keys_cert['certificateId'], 'thing_name': thing_name } logging.info("Thing:'{0}' associated with cert:'{1}'".format( thing_name, cert_arn)) device_policy = self.get_device_policy( device_name=thing_name, account_id=account_id, region=region ) self._create_attach_thing_policy(cert_arn, device_policy, iot_client, policy_name) config['devices'] = devices logging.info("create_devices cfg:{0}".format(config))
def create(self, group_type, config_file, group_name=None, region=None, profile_name=None): """ Create a Greengrass group in the given region. :param group_type: the type of group to create. Must match a `key` in the `group_types` dict :param config_file: config file of the group to create :param group_name: the name of the group. If no name is given, then group_type will be used. :param region: the region in which to create the new group. [default: us-west-2] :param profile_name: the name of the `awscli` profile to use. [default: None] """ logging.info("[begin] create command using group_types:{0}".format( self.group_types)) config = GroupConfigFile(config_file=config_file) if config.is_fresh() is False: raise ValueError( "Config file already tracking previously created group" ) if group_type not in self.group_types.keys(): raise ValueError("Can only create {0} groups.".format( self.group_types) ) if region is None: region = self._region # create an instance of the requested group type that uses the given # config file and region gt = self.group_types[group_type](config=config, region=region) # get and store the account's IoT endpoint for future use ep = _get_iot_session(region=region).describe_endpoint() misc = config['misc'] misc['iot_endpoint'] = ep['endpointAddress'] config['misc'] = misc # Create a Group logging.info("[begin] Creating a Greengrass Group") if group_name is None: group_name = group_type gg_client = _get_gg_session(region=region, profile_name=profile_name) group_info = gg_client.create_group(Name="{0}".format(group_name)) config['group'] = {"id": group_info['Id']} # setup the policies and roles gt.create_and_attach_thing_policy() gt.create_and_attach_iam_role() cl_arn = self._create_core_definition( gg_client=gg_client, group_type=gt, config=config, group_name=group_name ) dl_arn = self._create_device_definition( gg_client=gg_client, group_type=gt, config=config, group_name=group_name ) lv_arn = self._create_function_definition( gg_client=gg_client, group_type=gt, config=config ) log_arn = self._create_logger_definition( gg_client=gg_client, group_type=gt, config=config ) sub_arn = self._create_subscription_definition( gg_client=gg_client, group_type=gt, config=config ) logging.info( 'Group details, core_def:{0} device_def:{1} func_def:{2} ' 'logger_def:{3} subs_def:{4}'.format( cl_arn, dl_arn, lv_arn, log_arn, sub_arn) ) # Add all the constituent parts to the Greengrass Group group_args = {'GroupId': group_info['Id']} if cl_arn: group_args['CoreDefinitionVersionArn'] = cl_arn if dl_arn: group_args['DeviceDefinitionVersionArn'] = dl_arn if lv_arn: group_args['FunctionDefinitionVersionArn'] = lv_arn if log_arn: group_args['LoggerDefinitionVersionArn'] = log_arn if sub_arn: group_args['SubscriptionDefinitionVersionArn'] = sub_arn grp = gg_client.create_group_version( **group_args ) # store info about the provisioned artifacts into the local config file config['group'] = { "id": group_info['Id'], "version_arn": grp['Arn'], "version": grp['Version'], "name": group_name } logging.info( "[end] Created Greengrass Group {0}".format(group_info['Id']))
def create_core(self, thing_name, config_file, region=None, cert_dir=None, account_id=None, policy_name='ggc-default-policy', profile_name=None): """ Using the `thing_name` value, creates a Thing in AWS IoT, attaches and downloads new keys & certs to the certificate directory, then records the created information in the local config file for inclusion in the Greengrass Group as a Greengrass Core. :param thing_name: the name of the thing to create and use as a Greengrass Core :param config_file: config file used to track the Greengrass Core in the group :param region: the region in which to create the new core. [default: us-west-2] :param cert_dir: the directory in which to store the thing's keys and certs. If `None` then use the current directory. :param account_id: the account_id in which to create the new core. [default: None] :param policy_name: the name of the policy to associate with the device. [default: 'ggc-default-policy'] :param profile_name: the name of the `awscli` profile to use. [default: None] """ config = GroupConfigFile(config_file=config_file) if config.is_fresh() is False: raise ValueError( "Config file already tracking previously created core or group" ) if region is None: region = self._region if account_id is None: account_id = self._account_id keys_cert, thing = self.create_thing(thing_name, region, cert_dir) cert_arn = keys_cert['certificateArn'] config['core'] = { 'thing_arn': thing['thingArn'], 'cert_arn': cert_arn, 'cert_id': keys_cert['certificateId'], 'thing_name': thing_name } logging.debug("create_core cfg:{0}".format(config)) logging.info("Thing:'{0}' associated with cert:'{1}'".format( thing_name, cert_arn)) core_policy = self.get_core_policy(core_name=thing_name, account_id=account_id, region=region) iot_client = _get_iot_session(region=region, profile_name=profile_name) self._create_attach_thing_policy(cert_arn, core_policy, iot_client=iot_client, policy_name=policy_name) misc = config['misc'] misc['policy_name'] = policy_name config['misc'] = misc
def initialize(device_name, config_file, root_ca, certificate, private_key, group_ca_path): # read the config file cfg = GroupConfigFile(config_file) ggd_name = cfg['devices'][device_name]['thing_name'] iot_endpoint = cfg['misc']['iot_endpoint'] # prep for discovery dip = DiscoveryInfoProvider() dip.configureEndpoint(iot_endpoint) dip.configureCredentials(caPath=pa.root_ca, certPath=pa.certificate, keyPath=pa.private_key) dip.configureTimeout(10) # 10 sec logging.info("Discovery using CA:{0} cert:{1} prv_key:{2}".format( pa.root_ca, pa.certificate, pa.private_key)) discovered, discovery_info = utils.ggc_discovery( thing_name=ggd_name, discovery_info_provider=dip, max_groups=3) local, remote = _find_cores(cfg, discovery_info, iot_endpoint) # Save each group's CAs to use as a CA file later local_core_ca_file = utils.save_group_ca(local['ca'][0], group_ca_path, local['core'].groupId) for r in remote: remote[r]['ca_file'] = utils.save_group_ca(remote[r]['ca'][0], group_ca_path, remote[r]['core'].groupId) # create and connect MQTT client pointed toward the Master Greengrass Core mqttc_m = AWSIoTMQTTClient(ggd_name) log.info("[initialize] local gca_file:{0} cert:{1}".format( local_core_ca_file, certificate)) mqttc_m.configureCredentials(local_core_ca_file, private_key, certificate) mqttc_m.configureOfflinePublishQueueing(10, DROP_OLDEST) log.info("[initialize] Starting connection to Master Core") if utils.mqtt_connect(mqtt_client=mqttc_m, core_info=local['core']): log.info("[initialize] Connected to Master Core") else: log.error("[initialize] could not connect to Master Core") # create and connect MQTT clients pointed toward the remote Greengrass Cores mqttc_list = list() for r in remote: remote_mqttc = AWSIoTMQTTClient(ggd_name) log.info("[initialize] local gca_file:{0} cert:{1}".format( r, certificate)) remote_mqttc.configureCredentials(remote[r]['ca_file'], private_key, certificate) remote_mqttc.configureOfflinePublishQueueing(10, DROP_OLDEST) log.info("[initialize] Starting connection to Remote Core") if utils.mqtt_connect(mqtt_client=remote_mqttc, core_info=remote[r]['core']): log.info("[initialize] Connected to Remote Core:{0}".format( remote[r]['core'].coreThingArn)) mqttc_list.append(remote_mqttc) else: log.error( "[initialize] could not connect to Remote Core:{0}".format( remote[r]['core'].coreThingArn)) return mqttc_m, mqttc_list
def create(self, group_type, config_file, group_name=None, region='us-west-2'): """ Create a Greengrass group in the given region. :param group_type: the type of group to create. Must match a `key` in the `group_types` dict :param config_file: config file of the group to create :param group_name: the name of the group. If no name is given, then group_type will be used. :param region: the region in which to create the new group. [default: us-west-2] """ logging.info("[begin] create command using group_types:{0}".format( self.group_types)) config = GroupConfigFile(config_file=config_file) if config.is_fresh() is False: raise ValueError( "Config file already tracking previously created group" ) if group_type not in self.group_types.keys(): raise ValueError("Can only create {0} groups.".format( self.group_types) ) # create an instance of the requested group type that uses the given # config file and region gt = self.group_types[group_type](config=config, region=region) # Create a Group logging.info("[begin] Creating a Greengrass Group") if group_name is None: group_name = group_type gg_client = boto3.client("greengrass", region_name=region) group_info = gg_client.create_group(Name="{0}_group".format(group_name)) config['group'] = {"id": group_info['Id']} # setup the policies and roles gt.create_and_attach_thing_policy() gt.create_and_attach_iam_role() cl_arn = self._create_core_definition( gg_client=gg_client, group_type=gt, config=config, group_name=group_name ) dl_arn = self._create_device_definition( gg_client=gg_client, group_type=gt, config=config, group_name=group_name ) lv_arn = self._create_function_definition( gg_client=gg_client, group_type=gt, config=config ) log_arn = self._create_logger_definition( gg_client=gg_client, group_type=gt, config=config ) sub_arn = self._create_subscription_definition( gg_client=gg_client, group_type=gt, config=config ) # Add all the constituent parts to the Greengrass Group grp = gg_client.create_group_version( GroupId=group_info['Id'], CoreDefinitionVersionArn=cl_arn, DeviceDefinitionVersionArn=dl_arn, FunctionDefinitionVersionArn=lv_arn, LoggerDefinitionVersionArn=log_arn, SubscriptionDefinitionVersionArn=sub_arn ) # store info about the provisioned artifacts into the local config file config['group'] = { "id": group_info['Id'], "version_arn": grp['Arn'], "version": grp['Version'] } logging.info( "[end] Created Greengrass Group {0}".format(group_info['Id']))
def initialize(device_name, config_file, root_ca, certificate, private_key, group_ca_path): global ggd_name cfg = GroupConfigFile(config_file) local = dict() remote = dict() # determine heartbeat device's thing name and endpoint for MQTT clients ggd_name = cfg['devices'][device_name]['thing_name'] iot_endpoint = cfg['misc']['iot_endpoint'] # Discover Greengrass Core dip = DiscoveryInfoProvider() dip.configureEndpoint(iot_endpoint) dip.configureCredentials( caPath=root_ca, certPath=certificate, keyPath=private_key ) dip.configureTimeout(10) # 10 sec log.info("Discovery using CA: {0} certificate: {1} prv_key: {2}".format( root_ca, certificate, private_key )) # Now discover the groups in which this device is a member. # The arm should only be in two groups. The local and master groups. discovered, discovery_info = utils.ggc_discovery( ggd_name, dip, retry_count=10, max_groups=2 ) # Each group returned has a groupId which can compare to the configured # groupId in the config file. If the IDs match, the 'local' Group has been # found and therefore local core. # If the groupId's do not match, the 'remote' or 'master' group has been # found. group_list = discovery_info.getAllGroups() for g in group_list: logging.info("[initialize] group_id:{0}".format(g.groupId)) if g.groupId == cfg['group']['id']: local_cores = g.coreConnectivityInfoList local['core'] = local_cores[0] # just grab first core as local local['ca'] = g.caList else: remote_cores = g.coreConnectivityInfoList remote['core'] = remote_cores[0] # just grab first core as remote remote['ca'] = g.caList if len(local) > 1 and len(remote) > 1: logging.info("[initialize] local_core:{0} remote_core:{1}".format( local, remote )) else: raise EnvironmentError("Couldn't find the arm's Cores.") # just save one of the group's CAs to use as a CA file later local_core_ca_file = utils.save_group_ca( local['ca'][0], group_ca_path, local['core'].groupId ) remote_core_ca_file = utils.save_group_ca( remote['ca'][0], group_ca_path, remote['core'].groupId ) # Greengrass Cores discovered, now connect to Cores from this Device # get a client to send telemetry local_mqttc = AWSIoTMQTTClient(ggd_name) log.info("[initialize] local gca_file:{0} cert:{1}".format( local_core_ca_file, certificate)) local_mqttc.configureCredentials( local_core_ca_file, private_key, certificate ) local_mqttc.configureOfflinePublishQueueing(10, DROP_OLDEST) if not utils.mqtt_connect(mqtt_client=local_mqttc, core_info=local['core']): raise EnvironmentError("Connection to GG Core MQTT failed.") # get a shadow client to receive commands master_shadow_client = AWSIoTMQTTShadowClient(ggd_name) log.info("[initialize] remote ca_file:{0} cert:{1}".format( local_core_ca_file, certificate)) remote_mqttc = master_shadow_client.getMQTTConnection() remote_mqttc.configureCredentials( remote_core_ca_file, private_key, certificate ) if not utils.mqtt_connect(mqtt_client=master_shadow_client, core_info=remote['core']): raise EnvironmentError("Connection to Master Shadow failed.") # create and register the shadow handler on delta topics for commands # with a persistent connection to the Master shadow master_shadow = master_shadow_client.createShadowHandlerWithName( cfg['misc']['master_shadow_name'], True) log.info("[initialize] created handler for shadow name: {0}".format( cfg['misc']['master_shadow_name'] )) token = master_shadow.shadowGet(shadow_mgr, 5) log.info("[initialize] shadowGet() tk:{0}".format(token)) return local_mqttc, remote_mqttc, master_shadow