예제 #1
0
def main(options):
    try:
        # import mbed_cloud.update
        from mbed_cloud.update import UpdateAPI
        # from mbed_cloud.device_directory import DeviceDirectoryAPI
        import mbed_cloud.exceptions
    except:
        LOG.critical('manifest-tool update commands require installation of the Mbed Cloud SDK:'
                     ' https://github.com/ARMmbed/mbed-cloud-sdk-python')
        return 1

    LOG.debug('Preparing an update on Mbed Cloud')
    # upload a firmware
    api = None
    # dd_api = None
    try:
        # If set use api key set in manifest-tool update.
        if hasattr(options, 'api_key') and options.api_key:
            tempKey = options.api_key
            config = {'api_key': tempKey}
            api = UpdateAPI(config)
        # Otherwise use API key set in manifest-tool init
        else: api = UpdateAPI()
        # dd_api = DeviceDirectoryAPI()

    except ValueError:
        LOG.critical('API key is required to connect to the Update Service. It can be added using manifest-tool init -a'
                     ' <api key> or by manually editing .mbed_cloud_config.json')
        return 1
    if not options.payload_name:
        name = os.path.basename(options.payload.name) + time.strftime('-%Y-%m-%dT%H:%M:%S')
        LOG.info('Using {} as payload name.'.format(name))
        options.payload_name = name
    if len(options.payload_name) > MAX_NAME_LEN:
        LOG.critical(
            'Payload name is too long. Maximum length is {size}. ("{base}" <- {size}. overflow -> "{overflow}")'.format(
                size=MAX_NAME_LEN, base=options.payload_name[:MAX_NAME_LEN],
                overflow=options.payload_name[MAX_NAME_LEN:]))
        return 1

    if not options.manifest_name:
        name = os.path.basename(options.payload.name) + time.strftime('-%Y-%m-%dT%H:%M:%S-manifest')
        LOG.info('Using {} as manifest name.'.format(name))
        options.manifest_name = name

    if len(options.manifest_name) > MAX_NAME_LEN:
        LOG.critical(
            'Manifest name is too long. Maximum length is {size}. ("{base}" <- {size}. overflow -> "{overflow}")'.format(
                size=MAX_NAME_LEN, base=options.manifest_name[:MAX_NAME_LEN],
                overflow=options.manifest_name[MAX_NAME_LEN:]))
        return 1
    campaign_name = options.payload.name + time.strftime('-%Y-%m-%dT%H:%M:%S-campaign')
    if len(campaign_name) > MAX_NAME_LEN:
        LOG.critical(
            'Campaign name is too long. Maximum length is {size}. ("{base}" <- {size}. overflow -> "{overflow}")'.format(
                size=MAX_NAME_LEN, base=campaign_name[:MAX_NAME_LEN],
                overflow=campaign_name[MAX_NAME_LEN:]))
        return 1
    query_name = options.payload.name + time.strftime('-%Y-%m-%dT%H:%M:%S-filter')
    if len(query_name) > MAX_NAME_LEN:
        LOG.critical(
            'Filter name is too long. Maximum length is {size}. ("{base}" <- {size}. overflow -> "{overflow}")'.format(
                size=MAX_NAME_LEN, base=query_name[:MAX_NAME_LEN],
                overflow=query_name[MAX_NAME_LEN:]))
        return 1


    payload = None
    manifest = None
    campaign = None
    RC = 0
    handled = False
    manifest_file = None
    tempdirname = tempfile.mkdtemp()
    try:
        kwArgs = {}
        if options.payload_description:
            kwArgs['description'] = options.payload_description
        try:
            payload = api.add_firmware_image(
                            name = options.payload_name,
                        datafile = options.payload.name,
                        **kwArgs)
        except mbed_cloud.exceptions.CloudApiException as e:
            # TODO: Produce a better failuer message
            LOG.critical('Upload of payload failed with:\n{}'.format(e).rstrip())
            handled = True
            LOG.critical('Check API server URL "{}"'.format(api.config["host"]))
            raise e
        except MaxRetryError as e:
            LOG.critical('Upload of payload failed with:\n{}'.format(e))
            handled=True
            LOG.critical('Failed to establish connection to API-GW')
            LOG.critical('Check API server URL "{}"'.format(api.config["host"]))
            raise e

        LOG.info("Created new firmware at {}".format(payload.url))
        options.payload.seek(0)
        # create a manifest
        create_opts = copy.copy(options)
        create_opts.uri = payload.url
        create_opts.payload = options.payload
        if not (hasattr(create_opts, "output_file") and create_opts.output_file):
            try:
                manifest_file = open(os.path.join(tempdirname,'manifest'),'wb')
                LOG.info("Created temporary manifest file at {}".format(manifest_file.name))
                create_opts.output_file = manifest_file
            except IOError as e:
                LOG.critical("Failed to create temporary manifest file with:")
                print(e)
                LOG.critical("Try using '-o' to output a manifest file at a writable location.")
                handled = True
                raise e
        try:
            rc = create.main(create_opts)
        except IOError as e:
            LOG.critical("Failed to create manifest with:")
            print(e)
            handled = True
            raise e

        if rc:
            return rc

        kwArgs = {}
        if options.manifest_description:
            kwArgs['description'] = options.manifest_description

        manifest_path = create_opts.output_file.name
        create_opts.output_file.close()
        # upload a manifest
        try:
            manifest = api.add_firmware_manifest(
                            name = options.manifest_name,
                        datafile = manifest_path,
                        **kwArgs)
        except mbed_cloud.exceptions.CloudApiException as e:
            # TODO: Produce a better failure message
            LOG.critical('Upload of manifest failed with:')
            print(e)
            LOG.critical("Try using '-o' to output a manifest file at a writable location.")
            handled = True
            raise e

        LOG.info('Created new manifest at {}'.format(manifest.url))
        LOG.info('Manifest ID: {}'.format(manifest.id))

        try:
            campaign = api.add_campaign(
                name = campaign_name,
                manifest_id = manifest.id,
                device_filter = {'id': { '$eq': options.device_id }},
            )
        except mbed_cloud.exceptions.CloudApiException as e:
            LOG.critical('Campaign creation failed with:')
            print(e)
            handled = True
            raise e

        LOG.info('Campaign successfully created. Current state: %r' % (campaign.state))
        LOG.info('Campaign successfully created. Filter result: %r' % (campaign.device_filter))

        LOG.info("Starting the update campign...")

        # By default a new campaign is created with the 'draft' status. We can manually start it.
        try:
            new_campaign = api.start_campaign(campaign)
            new_campaign = None
        except mbed_cloud.exceptions.CloudApiException as e:
            LOG.critical('Starting campaign failed with:')
            print(e)
            handled = True
            raise e

        LOG.info("Campaign successfully started. Current state: %r. Checking updates.." % (campaign.state))
        oldstate = api.get_campaign(campaign.id).state
        LOG.info("Current state: %r" % (oldstate))
        timeout = options.timeout
        while timeout != 0:
            c = api.get_campaign(campaign.id)
            if oldstate != c.state:
                LOG.info("Current state: %r" % (c.state))
                oldstate = c.state
            if c.state in STOP_STATES:
                LOG.info("Finished in state: %r" % (c.state))
                break
            time.sleep(1)
            if timeout > 0:
                timeout -= 1
        if timeout == 0:
            LOG.critical("Campaign timed out")
            RC = 1

    except KeyboardInterrupt as e:
        LOG.critical('User Aborted... Cleaning up.')
        RC = 1
    except:
        if not handled:
            LOG.critical('Unhandled Exception:')
            import traceback
            traceback.print_exc(file=sys.stdout)
        RC = 1
    finally:
        # cleanup
        if manifest_file:
            manifest_file.close()
        shutil.rmtree(tempdirname)
        if not options.no_cleanup:
            LOG.info("** Deleting update campaign and manifest **")
            try:
                if campaign and campaign.id:
                    api.delete_campaign(campaign.id)
                if manifest and manifest.id:
                    api.delete_firmware_manifest(manifest.id)
                if payload and payload.id:
                    api.delete_firmware_image(payload.id)
                # dd_api.delete_query(new_query.id)
            except mbed_cloud.exceptions.CloudApiException as e:
                LOG.critical('Cleanup of campaign failed with:')
                print(e)
                RC = 1
    return RC
예제 #2
0
def update(
        payload_path: Path,
        dev_cfg: dict,
        manifest_version: Type[ManifestAsnCodecBase],
        priority: int,
        vendor_data: Path,
        device_id: str,
        do_wait: bool,
        do_start: bool,
        timeout: int,
        skip_cleanup: bool,
        service_config: Path,
        fw_version: str,
        sign_image: bool,
        component: str
):
    config = None
    if service_config.is_file():
        with service_config.open('rt') as fh:
            config = yaml.safe_load(fh)

    api = UpdateAPI(config)
    manifest_path = None
    payload_cloud = None
    manifest_cloud = None
    campaign_cloud = None
    try:
        timestamp = time.strftime('%Y_%m_%d-%H_%M_%S')
        payload_cloud = _upload_payload(
            api,
            payload_name='{timestamp}-{filename}'.format(
                filename=payload_path.name,
                timestamp=timestamp),
            payload_path=payload_path
        )

        manifest_data = create_dev_manifest(
            dev_cfg=dev_cfg,
            manifest_version=manifest_version,
            vendor_data_path=vendor_data,
            payload_path=payload_path,
            payload_url=payload_cloud.url,
            priority=priority,
            fw_version=fw_version,
            sign_image=sign_image,
            component=component
        )

        manifest_name = 'manifest-{timestamp}-{filename}'.format(
            filename=payload_path.name,
            timestamp=timestamp)
        manifest_path = payload_path.parent / manifest_name

        manifest_path.write_bytes(manifest_data)

        manifest_cloud = _upload_manifest(api, manifest_name, manifest_path)

        campaign_name = 'campaign-{timestamp}-{filename}'.format(
            filename=payload_path.name,
            timestamp=timestamp)

        campaign_cloud = _create_campaign(
            api,
            campaign_name,
            manifest_cloud,
            dev_cfg['vendor-id'],
            dev_cfg['class-id'],
            device_id
        )

        if do_start:
            _start_campaign(api, campaign_cloud)

        if do_wait:
            _wait(api, campaign_cloud, timeout)

    finally:
        if not skip_cleanup and do_wait:
            try:
                logger.info('Cleaning up resources.')
                if campaign_cloud:
                    logger.info('Deleting campaign %s', campaign_cloud.id)
                    api.delete_campaign(campaign_cloud.id)
                if manifest_cloud:
                    logger.info('Deleting FW manifest %s', manifest_cloud.id)
                    api.delete_firmware_manifest(manifest_cloud.id)
                if payload_cloud:
                    logger.info('Deleting FW image %s', payload_cloud.id)
                    api.delete_firmware_image(payload_cloud.id)
                if manifest_path and manifest_path.is_file():
                    manifest_path.unlink()
            except CloudApiException:
                logger.error('Failed to cleanup resources')