Ejemplo n.º 1
0
def get_drone_mode_tvs(request_context,
                       async_kvstore_client,
                       user=None,
                       device_ids=None):
    """Fetch list of tvs from the drone_mode_tvs collection"""

    params = None
    if not user:
        user = request_context.current_user

    if device_ids:
        urlsafe_device_ids = [
            b64_to_urlsafe_b64(device_id) for device_id in device_ids
        ]
        query = build_containedin_clause(constants.KEY, urlsafe_device_ids)

        params = {constants.QUERY: json.dumps(query)}

    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.DRONE_MODE_TVS_COLLECTION_NAME,
        params=params,
        owner=user,
        auth_header=request_context.system_auth_header)

    yield check_and_raise_error(response, request_context,
                                'Get Drone Mode TVs')
    tvs = yield response.json()

    LOGGER.debug('get_drone_mode_tvs returned=%s', tvs)
    defer.returnValue(tvs)
def activate_tv_bookmark(request_context, async_client_factory, bookmark_name):
    """
    Function to activate a TV bookmark
    :param request_context: request context used to make kvstore requests
    :param async_kvstore_client: async client used to make kvstore requests
    :param bookmark_name: tv bookmark name
    """
    # check for bookmark with existing name
    async_kvstore_client = async_client_factory.kvstore_client()
    bookmark_json = yield fetch_tv_bookmark(bookmark_name,
                                            request_context,
                                            async_kvstore_client,
                                            error_if_empty=True)
    LOGGER.debug('bookmark_json used for activating tv bookmark=%s',
                 bookmark_json)
    bookmark_json_items = bookmark_json.get(constants.TV_CONFIG_MAP,
                                            {}).items()

    # update timestamp when activating
    device_ids = set()
    for device_id, item in bookmark_json_items:
        item[constants.TIMESTAMP] = datetime.now().strftime('%s')
        device_ids.add(device_id)
    errors = yield validate_devices(device_ids, request_context,
                                    async_kvstore_client)

    # create deep copy of config data so original doesn't get modified
    post_data = deepcopy(bookmark_json_items)

    # remove device id and add key to dict
    for device_id, config in post_data:
        LOGGER.debug('device_id=%s, config=%s', device_id, config)
        if constants.DEVICE_ID in config:
            del config[constants.DEVICE_ID]
        kvstore_key = b64_to_urlsafe_b64(device_id)
        config[constants.KEY] = kvstore_key

    device_ids = [device_id for device_id, _ in post_data]
    data = [config for _, config in post_data]

    # Update drone mode tvs kvstore collection with tv config data
    yield async_kvstore_client.async_batch_save_request(
        request_context.auth_header,
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        data,
        owner=request_context.current_user)

    # Send config updates to all devices in the bookmark and
    # all ipads registered to the current user
    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=device_ids)
    if errors:
        LOGGER.error(
            'Errors occurred during TV Bookmark Activate Request for bookmark_name=%s, errors=%s',
            bookmark_name, errors)

        defer.returnValue(errors)

    LOGGER.info('Successful TV Bookmark Activate Request for bookmark_name=%s',
                bookmark_name)
    defer.returnValue(None)
def process_subscribe_drone_mode_ipad(request_context,
                                      client_subscription_message,
                                      server_subscription_response,
                                      async_client_factory):
    """
    Process subscribe to drone mode ipad events for a device id

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    LOGGER.debug('start of subscribe drone mode ipad')
    async_kvstore_client = async_client_factory.kvstore_client()

    LOGGER.debug('request_context=%s', request_context)
    # base64 encode device id
    device_id = request_context.device_id
    urlsafe_b64encoded_device_id = b64_to_urlsafe_b64(device_id)
    query = {
        constants.QUERY: json.dumps({
            constants.DEVICE_ID: device_id,
        }),
        constants.LIMIT: 1
    }

    # fetch device from registered_devices_table to get device name
    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.REGISTERED_DEVICES_COLLECTION_NAME,
        params=query,
        owner=request_context.current_user,
        auth_header=request_context.auth_header)

    yield check_and_raise_error(response, request_context, 'Fetch registered devices')

    device = yield response.json()
    if device:
        device = device[0]
    else:
        raise SpacebridgeApiRequestError('Invalid device id={}'.format(device_id), status_code=http.BAD_REQUEST)
    device_name = device[constants.DEVICE_NAME]

    # write to ipads collection
    ipad_json = {constants.DEVICE_NAME: device_name}
    response = yield async_kvstore_client.async_kvstore_post_or_update_request(
        constants.DRONE_MODE_IPADS_COLLECTION_NAME,
        json.dumps(ipad_json),
        request_context.system_auth_header,
        key_id=urlsafe_b64encoded_device_id,
        owner=request_context.current_user)

    # construct parameters for subscription creation / updating
    ttl_seconds = client_subscription_message.clientSubscribeRequest.ttlSeconds
    expiration_time = get_expiration_timestamp_str(ttl_seconds=ttl_seconds)
    now = get_current_timestamp_str()
    params = {
        constants.TTL_SECONDS: str(ttl_seconds),
        constants.DEVICE_ID: device_id,
        constants.LAST_UPDATE_TIME: now,
        constants.EXPIRED_TIME: expiration_time,
        constants.USER: request_context.current_user,
        constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_IPAD
    }
    # delete existing subscriptions
    query = {
        constants.QUERY: json.dumps({
            constants.DEVICE_ID: device_id,
            constants.USER: request_context.current_user,
            constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_IPAD
        })
    }
    delete_response = yield async_kvstore_client.async_kvstore_delete_request(
        collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
        params=query,
        owner=constants.NOBODY,
        auth_header=request_context.auth_header)
    yield check_and_raise_error(delete_response, request_context, 'Delete existing ipad subscriptions')

    # create subscription
    subscription_id = yield write_drone_mode_subscription(request_context,
                                                          params,
                                                          async_client_factory)
    server_subscription_response.subscriptionId = subscription_id
    server_subscription_response.serverSubscribeResponse.droneModeiPadSubscribe.SetInParent()
    LOGGER.debug('Successfully wrote drone mode ipad subscription with id=%s', subscription_id)
def process_tv_config_delete_request(request_context,
                                     client_single_request,
                                     single_server_response,
                                     async_client_factory):
    """This method will process a tvConfigDeleteRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """
    async_kvstore_client = async_client_factory.kvstore_client()

    tv_config_delete_request = client_single_request.tvConfigDeleteRequest

    device_ids = tv_config_delete_request.deviceId

    LOGGER.debug('device_ids to delete=%s', device_ids)
    yield validate_devices(set(device_ids), request_context, async_kvstore_client)
    # need to fetch configs before deleting to make sure we're not deleting the captain
    # and orphaning the workers, if so, for each one that is a captain, 
    # we need to fetch the other configs in the grid
    # delete the to be deleted captain from the device_id list, and update
    # the existing members of the grid

    tvs = yield get_drone_mode_tvs(request_context, async_kvstore_client, device_ids=device_ids)
    # fetch configs from kvstore
    # check to see if they are currently captain
    for tv in tvs:
        raw_id = base64.urlsafe_b64decode(str(tv[constants.KEY]))
        encoded_id = b64encode_to_str(raw_id)
        tv[constants.DEVICE_ID] = encoded_id

        if (has_grid(tv, is_json=True)):
            tv_config = TVConfig(**tv)
            grid_ids = tv.get(constants.TV_GRID, {}).get(constants.DEVICE_IDS, [])
            yield remove_from_grid(tv_config=tv_config,
                                   device_ids=grid_ids,
                                   request_context=request_context,
                                   async_kvstore_client=async_kvstore_client)

    entries_to_delete = []

    for device_id in device_ids:
        kvstore_key = b64_to_urlsafe_b64(device_id)
        post_data = {constants.KEY: kvstore_key}
        entries_to_delete.append(post_data)

    deleted_ids = yield async_kvstore_client.async_batch_save_request(
        request_context.system_auth_header,
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        entries_to_delete,
        owner=request_context.current_user)
    deleted_device_ids = [urlsafe_b64_to_b64(deleted_id) for deleted_id in deleted_ids]
    if deleted_device_ids:
        single_server_response.tvConfigDeleteResponse.deletedIds.extend(deleted_device_ids)

    if not deleted_ids:
        raise SpacebridgeApiRequestError('None of the device_ids={} were deleted'.format(device_ids), status_code=http.INTERNAL_SERVER_ERROR)

    elif len(deleted_ids) != len(device_ids):
        kvstore_keys = {b64_to_urlsafe_b64(device_id) for device_id in device_ids}
        LOGGER.error('TV configs with these ids: %s were deleted, '
                     'while TV configs with these ids: %s were not.',
                     deleted_ids, list(kvstore_keys-set(deleted_ids)))
    # update current deleted tv subscriptions and
    # all ipads registered to current user

    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=device_ids)


    LOGGER.info('Successful TV Config Delete Request for device_ids=%s', deleted_device_ids)
def process_tv_config_set_request(request_context,
                                  client_single_request,
                                  single_server_response,
                                  async_client_factory):
    """
    This method will process a tvConfigSetRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """
    async_kvstore_client = async_client_factory.kvstore_client()
    tv_config_set_request = client_single_request.tvConfigSetRequest

    tv_config_proto = tv_config_set_request.tvConfig
    tv_config = TVConfig()
    tv_config.from_protobuf(tv_config_proto)
    timestamp = get_current_timestamp_str()
    ids_to_update = [tv_config.device_id]
    # Check to make sure device is valid before proceeding
    yield validate_devices(set(ids_to_update), request_context, async_kvstore_client)

    # Before setting config, we need to check if
    # we're setting a config on a tv that is currently in
    # a grid configuration, so we need to fetch it.
    tvs = yield get_drone_mode_tvs(request_context,
                                   async_kvstore_client,
                                   device_ids=[tv_config.device_id])

    if tvs:
        tv = tvs[0]
        existing_config = TVConfig(**tv)
        # If it's a grid, we remove it from the existing grid list,
        if has_grid(tv, is_json=True) and not has_grid(tv_config_proto):
            device_ids = ids_to_update = existing_config.tv_grid[constants.DEVICE_IDS]
            yield remove_from_grid(tv_config=existing_config,
                                   device_ids=device_ids,
                                   request_context=request_context,
                                   async_kvstore_client=async_kvstore_client,
                                   timestamp=timestamp)

    # set timestamp here
    tv_config.timestamp = timestamp
    tv_config_json = json.loads(tv_config.to_json())

    # delete device id
    if constants.DEVICE_ID in tv_config_json:
        del tv_config_json[constants.DEVICE_ID]

    LOGGER.debug('tv_config_json=%s', tv_config_json)

    # convert device id to urlsafe b64 encoded to use as kvstore key
    urlsafe_b64encoded_device_id = b64_to_urlsafe_b64(tv_config.device_id)
    tv_config_json[constants.KEY] = urlsafe_b64encoded_device_id

    response = yield async_kvstore_client.async_kvstore_post_or_update_request(
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        json.dumps(tv_config_json),
        request_context.system_auth_header,
        key_id=urlsafe_b64encoded_device_id,
        owner=request_context.current_user)

    yield check_and_raise_error(response,
                                request_context,
                                'Write TV Config',
                                valid_codes=[http.OK, http.CREATED])

    single_server_response.tvConfigSetResponse.SetInParent()
    # update tv subscription for config we just created
    # (and affected grid device ids, if applicable) as well as
    # all ipads registered to current user
    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=ids_to_update)

    LOGGER.info("Successful TV Config Set Request for device id=%s", tv_config.device_id)
def process_tv_config_bulk_set_request(request_context,
                                       client_single_request,
                                       single_server_response,
                                       async_client_factory):
    """
    Bulk tv config set request.  Used to bulk send tv config set requests
    when we're setting a grid configuration

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    async_kvstore_client = async_client_factory.kvstore_client()
    tv_config_protos = client_single_request.tvConfigBulkSetRequest.tvConfig
    is_token_update = client_single_request.tvConfigBulkSetRequest.isTokenUpdate
    LOGGER.debug('tv_config_protos=%s', tv_config_protos)
    device_ids = {proto.device_id for proto in tv_config_protos}
    timestamp = get_current_timestamp_str()
    warnings = yield validate_devices(device_ids, request_context, async_kvstore_client)
    # Before setting configs, we need to check if
    # we're setting configs on tvs that are currently in
    # grid configurations, so we need to fetch them.
    # This logic is ignored if we're doing a token update

    existing_grids_to_update = set()
    if not is_token_update:
        tvs = yield get_drone_mode_tvs(request_context,
                                       async_kvstore_client,
                                       device_ids=device_ids)
        if tvs:
            requests = []
            for tv in tvs:
                existing_config = TVConfig(**tv)
                # If it's a grid, we remove it from the existing grid list
                if has_grid(tv, is_json=True):
                    grid_device_ids = existing_config.tv_grid[constants.DEVICE_IDS]
                    existing_grids_to_update.update(grid_device_ids)
                    request = remove_from_grid(tv_config=existing_config,
                                               device_ids=grid_device_ids,
                                               request_context=request_context,
                                               async_kvstore_client=async_kvstore_client,
                                               timestamp=timestamp)
                    requests.append(request)

            if requests:
                exceptions = []
                responses = yield defer.DeferredList(requests, consumeErrors=True)
                for response in responses:
                    if isinstance(response[1], Failure):
                        exceptions.append(response[1])
                LOGGER.debug('Finished updating modified tvs')
                if exceptions:
                    LOGGER.error('Encountered exceptions updating tvs, e=%s', exceptions)
                    # if we couldn't update any of the existing configs, we should not continue the request
                    if len(exceptions) == len(requests):
                        raise SpacebridgeApiRequestError('Unable to update all existing tv configs',
                                                         status_code=http.INTERNAL_SERVER_ERROR)



    post_data = []
    for tv_config_proto in tv_config_protos:
        tv_config = TVConfig()
        tv_config.from_protobuf(tv_config_proto)

        # set timestamp here
        tv_config.timestamp = timestamp
        tv_config_json = json.loads(tv_config.to_json())

        # delete device id
        if constants.DEVICE_ID in tv_config_json:
            del tv_config_json[constants.DEVICE_ID]

        LOGGER.debug('tv_config_json=%s', tv_config_json)

        # convert device id to urlsafe b64 encoded to use as kvstore key
        urlsafe_b64encoded_device_id = b64_to_urlsafe_b64(tv_config.device_id)
        tv_config_json[constants.KEY] = urlsafe_b64encoded_device_id
        post_data.append(tv_config_json)
    LOGGER.debug('post_data=%s', post_data)
    updated_keys = yield async_kvstore_client.async_batch_save_request(
        request_context.system_auth_header,
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        post_data,
        owner=request_context.current_user)
    ids_to_update = {urlsafe_b64_to_b64(key) for key in updated_keys}
    ids_to_update.update(existing_grids_to_update)
    ids_to_update = list(ids_to_update)

    LOGGER.debug('ids_to_update=%s', ids_to_update)
    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=ids_to_update)
    if warnings:
        single_server_response.tvConfigBulkSetResponse.warnings.extend(warnings)
    single_server_response.tvConfigBulkSetResponse.SetInParent()