Exemplo n.º 1
0
  def __init__(self, auth_token):
    """Get a reference to a logger object.

      Args:
      auth_token: the authentication token to use for GCP requests
    """
    self.auth_token = auth_token
    self.logger = _log.GetLogger('LogoCert')
    self.transport = Transport(self.logger)
Exemplo n.º 2
0
    def __init__(self, logger):
        """Get a reference to a logger object and JsonParser.

    Args:
        logger: initialized logger object.
    """
        self.logger = logger
        self.storage = Storage(Constants.AUTH['CRED_FILE'])
        self.transport = Transport(self.logger)
Exemplo n.º 3
0
    def __init__(self, logger, auth_token, gcp, model=None, privet_port=None):
        """Initialize a device object.

    Args:
      logger: initialized logger object.
      auth_token: string, auth_token of authenticated user.
      gcp: initialized GCPService object
      model: string, unique model or name of device.
      privet_port: integer, tcp port devices uses for Privet protocol.
    """
        if model:
            self.model = model
        else:
            self.model = Constants.PRINTER['MODEL']

        self.auth_token = auth_token
        self.logger = logger
        self.transport = Transport(logger)
        self.ipv4 = Constants.PRINTER['IP']
        if privet_port:
            self.port = privet_port
        else:
            self.port = Constants.PRINTER['PORT']
        self.dev_id = None
        self.name = Constants.PRINTER['NAME']
        self.gcp = gcp
        self.status = None
        self.messages = {}
        self.details = {}
        self.error_state = False
        self.warning_state = False
        self.cdd = {}
        self.supported_types = None
        self.info = None

        self.url = 'http://%s:%s' % (self.ipv4, self.port)
        self.logger.info('Device URL: %s', self.url)
        self.transport = Transport(logger)
        self.headers = None
        self.privet = Privet(logger)
        self.privet_url = self.privet.SetPrivetUrls(self.ipv4, self.port)
        self.GetPrivetInfo()
Exemplo n.º 4
0
    def __init__(self, logger, creds, Constants):
        """ Use initialized objects from main module.
    
    Args:
      logger: initialized logger object.
      creds: OAuth2Credentials.
      Constants: object holding constant values.
    """
        self.logger = logger
        self.creds = creds
        self.transport = Transport(self.logger)

        self.token = gdata.gauth.OAuth2Token(
            client_id=Constants.USER['CLIENT_ID'],
            client_secret=Constants.USER['CLIENT_SECRET'],
            scope=Constants.AUTH['SCOPE'],
            access_token=Constants.AUTH['ACCESS'],
            refresh_token=Constants.AUTH['REFRESH'],
            user_agent=Constants.AUTH['USER_AGENT'])
        self.client = gdata.spreadsheets.client.SpreadsheetsClient()
        self.token.authorize(self.client)
Exemplo n.º 5
0
    def __init__(self, logger, chromedriver, model=None, privet_port=None):
        """Initialize a device object.

    Args:
      logger: initialized logger object.
      chromedriver: initialized chromedriver object.
      model: string, unique model or name of device.
      privet_port: integer, tcp port devices uses for Privet protocol.
    """
        if model:
            self.model = model
        else:
            self.model = Constants.PRINTER['MODEL']
        self.logger = logger
        self.cd = chromedriver
        self.cloudprintmgr = CloudPrintMgr(logger, chromedriver)
        self.ipv4 = Constants.PRINTER['IP']
        if privet_port:
            self.port = privet_port
        else:
            self.port = Constants.PRINTER['PORT']
        self.name = Constants.PRINTER['NAME']
        self.status = None
        self.messages = {}
        self.details = {}
        self.error_state = False
        self.warning_state = False
        self.cdd = {}
        self.info = None

        self.url = 'http://%s:%s' % (self.ipv4, self.port)
        self.logger.info('Device URL: %s', self.url)
        self.transport = Transport(logger)
        self.jparser = JsonParser(logger)
        self.headers = None
        self.privet = Privet(logger)
        self.privet_url = self.privet.SetPrivetUrls(self.ipv4, self.port)
        self.GetPrivetInfo()
Exemplo n.º 6
0
class GoogleDataMgr(object):
    """An object to interact with Google Drive and Docs."""
    def __init__(self, logger, creds, Constants):
        """ Use initialized objects from main module.
    
    Args:
      logger: initialized logger object.
      creds: OAuth2Credentials.
      Constants: object holding constant values.
    """
        self.logger = logger
        self.creds = creds
        self.transport = Transport(self.logger)

        self.token = gdata.gauth.OAuth2Token(
            client_id=Constants.USER['CLIENT_ID'],
            client_secret=Constants.USER['CLIENT_SECRET'],
            scope=Constants.AUTH['SCOPE'],
            access_token=Constants.AUTH['ACCESS'],
            refresh_token=Constants.AUTH['REFRESH'],
            user_agent=Constants.AUTH['USER_AGENT'])
        self.client = gdata.spreadsheets.client.SpreadsheetsClient()
        self.token.authorize(self.client)

    def CreateSheet(self, name):
        """Create a Google Spreadsheet to hold test results.

    Args:
      name: string, name to assign the spreadsheet.
    Returns:
      void - this should not fail
    """
        service = discovery.build('sheets',
                                  'v4',
                                  http=self.creds.authorize(Http()))
        data = {'properties': {'title': name}}
        res = service.spreadsheets().create(body=data).execute()
        if res is not None and 'spreadsheetId' in res:
            self.logger.info(
                'Created a new google spreadsheet with sheets API.')

            self.addConditionalFormatting(service, res['spreadsheetId'])
            if Constants.TEST['SHARE_SHEET_WITH_GOOGLE']:
                self.logger.info(
                    'Attempting to share newly created sheet with Google')
                self.ShareSheet(res['spreadsheetId'])
        else:
            self.logger.info('Failed to create a new google spreadhseet with '
                             'sheets API')

    def addConditionalFormatting(self, service, id):
        """Add conditional formatting for sheet.

    Args:
      service: Object, resource for interacting with google API
      id: String, spreadsheet ID
    """

        requests = []

        # Rule 1 - PASS is green
        passed_rule = self.getBGColorRule('Passed', rgb=(183, 225, 205))
        failed_rule = self.getBGColorRule('Failed', rgb=(244, 199, 195))
        skipped_rule = self.getBGColorRule('Skipped', rgb=(252, 232, 178))

        requests.append(passed_rule)
        requests.append(failed_rule)
        requests.append(skipped_rule)

        body = {'requests': requests}

        request = service.spreadsheets().batchUpdate(spreadsheetId=id,
                                                     body=body)
        request.execute()

    def getBGColorRule(self, text_to_match, rgb):
        """Get conditional formatting rule object for Column C of sheet.

    Args:
      text_to_match: String, the text to conditionally format for
      rgb: tuple, RGB values (out of 255)
    """

        return {
            'addConditionalFormatRule': {
                'rule': {
                    'ranges': [{
                        'sheetId': 0,
                        'startRowIndex': 0,
                        'endRowIndex': 9999,
                        'startColumnIndex': 2,
                        'endColumnIndex': 3
                    }],
                    'booleanRule': {
                        'condition': {
                            'type': 'TEXT_EQ',
                            'values': [{
                                'userEnteredValue': text_to_match
                            }]
                        },
                        'format': {
                            'backgroundColor': {
                                'red': rgb[0] / 255.0,
                                'green': rgb[1] / 255.0,
                                'blue': rgb[2] / 255.0
                            }
                        }
                    }
                }
            }
        }

    def ShareSheet(self, id):
        """Share a Google Spreadsheet with Google's GCP certification team.

    Args:
      id: string, Google Sheet id.
    """
        url = 'https://www.googleapis.com/drive/v2/files/%s/permissions' % id

        data = {
            'value': Constants.TEST['GCP_TEAM_EMAIL'],
            'type': 'user',
            'role': 'writer',
        }

        params = {
            'sendNotificationEmails':
            True,
            'emailMessage':
            ('New Logocert google sheet shared with you by %s' %
             Constants.PRINTER['MANUFACTURER'])
        }

        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer %s' % self.token.access_token
        }

        r = self.transport.HTTPPost(url,
                                    data=json.dumps(data),
                                    params=params,
                                    headers=headers)

        if r is None:
            self.logger.error('Google Drive API returned None response')
            raise

        if r.status_code == 200:
            self.logger.info(
                'Successfully sharing sheet (R/W) with Google: %s' %
                Constants.TEST['GCP_TEAM_EMAIL'])
            return

        if r.status_code == 403:
            print r.json()['error']['message']
            self.logger.error('API Disabled. Follow the steps below to fix')
            self.logger.error('1) Enable Drive API via link above.')
            self.logger.error('2) Delete the newly created spreadsheet and '
                              're-run this tool.')
        else:
            self.logger.error('ERROR: Google Drive Permission API failed with '
                              'status: %s' % r.status_code)
            print r.text
        sys.exit()

    def GetSpreadSheetID(self, name):
        """Return the spreadsheet id that has the matching name.

    Args:
      name: string, name (title) of spreadsheet.
    Returns:
      string, the spreadsheet id.
    """
        q = gdata.spreadsheets.client.SpreadsheetQuery(name, 'true')
        try:
            feed = self.client.GetSpreadsheets(query=q)
        except gdata.service.RequestError:
            self.logger.error('Error getting spreadsheet feed.')
            return None

        try:
            sid = feed.entry[0].id.text.rsplit('/', 1)[1]
        except IndexError:
            sid = None
        return sid

    def GetWorkSheetID(self, spreadsheet_id):
        """Return the worksheet id of the spreadsheet with spreadsheet_id.

    Args:
      spreadsheet_id: string, id of existing spreadsheet.
    Returns:
      string, id of worksheet.
    """
        try:
            feed = self.client.GetWorksheets(spreadsheet_id)
        except gdata.service.RequestError:
            self.logger.error('Error getting worksheet feed.')
            return None

        try:
            wid = feed.entry[0].id.text.rsplit('/', 1)[1]
        except IndexError:
            wid = None
        return wid

    def CreateColumnHeaders(self, headers, spreadsheet_id, worksheet_id):
        """Create column headers (columns in row 1) in a spreadsheet.

    Args:
      headers: list of strings for spreadsheet column headers.
      spreadsheet_id: string, id of spreadsheet.
      worksheet_id: string, id of worksheet.
    Returns:
      boolean: True = headers created, False = errors.
    """
        cell_range = 'A1:H1'
        cellq = gdata.spreadsheets.client.CellQuery(range=cell_range,
                                                    return_empty='true')
        cells = self.client.GetCells(spreadsheet_id, worksheet_id, q=cellq)
        batch = gdata.spreadsheets.data.BuildBatchCellsUpdate(
            spreadsheet_id, worksheet_id)
        i = 0
        for cell in cells.entry:
            cell.cell.input_value = headers[i]
            batch.add_batch_entry(cell,
                                  cell.id.text,
                                  batch_id_string=cell.title.text,
                                  operation_string='update')
            i += 1
        try:
            self.client.batch(batch, force=True)
        except gdata.service.RequestError:
            self.logger.error('Error adding header in column %d', i)
            return False
        return True

    def AddRowUsingColumnHeaders(self, headers, row_data, spreadsheet_id,
                                 worksheet_id):
        """Add a row to an existing spreadsheet with headers.

    Args:
      headers: list of strings that match the column headers.
      row_data: list of strings, 1 for each column.
      spreadsheet_id: string, id of spreadsheet.
      worksheet_id: string, id of worksheet.
    Returns:
      boolean: True = row added, False = errors.
    """
        row = dict(zip(headers, row_data))
        entry = gdata.spreadsheets.data.ListEntry()
        for k in row:
            entry.set_value(k, row[k])
        try:
            self.client.add_list_entry(entry, spreadsheet_id, worksheet_id)
        except gdata.service.RequestError:
            self.logger.error('Error inserting a new row into spreadsheet.')
            return False
        return True
Exemplo n.º 7
0
class Device(object):
    """The basic device object."""
    def __init__(self, logger, auth_token, gcp, model=None, privet_port=None):
        """Initialize a device object.

    Args:
      logger: initialized logger object.
      auth_token: string, auth_token of authenticated user.
      gcp: initialized GCPService object
      model: string, unique model or name of device.
      privet_port: integer, tcp port devices uses for Privet protocol.
    """
        if model:
            self.model = model
        else:
            self.model = Constants.PRINTER['MODEL']

        self.auth_token = auth_token
        self.logger = logger
        self.transport = Transport(logger)
        self.ipv4 = Constants.PRINTER['IP']
        if privet_port:
            self.port = privet_port
        else:
            self.port = Constants.PRINTER['PORT']
        self.dev_id = None
        self.name = Constants.PRINTER['NAME']
        self.gcp = gcp
        self.status = None
        self.messages = {}
        self.details = {}
        self.error_state = False
        self.warning_state = False
        self.cdd = {}
        self.supported_types = None
        self.info = None

        self.url = 'http://%s:%s' % (self.ipv4, self.port)
        self.logger.info('Device URL: %s', self.url)
        self.transport = Transport(logger)
        self.headers = None
        self.privet = Privet(logger)
        self.privet_url = self.privet.SetPrivetUrls(self.ipv4, self.port)
        self.GetPrivetInfo()

    def GetPrivetInfo(self):
        self.privet_info = {}
        info = self.Info()
        if info is not None:
            for key in info:
                self.privet_info[key] = info[key]
                self.logger.debug('Privet Key: %s', key)
                self.logger.debug('Value: %s', info[key])
                self.logger.debug('--------------------------')
            if 'x-privet-token' in info:
                self.headers = {'X-Privet-Token': str(info['x-privet-token'])}

    def Register(self,
                 msg,
                 user=Constants.USER['EMAIL'],
                 use_token=True,
                 no_action=False,
                 wait_for_user=True):
        """Register device using Privet.
    Args:
      msg: string, the instruction for the user about the registration
                   confirmation dialog on the printer
      user: string, the user to register for
      use_token: boolean, use auth_token if True
      no_action: boolean, if True, do not wait
      wait_for_user: boolean, if True, wait for user to press UI button
    Returns:
      boolean: True = device registered, False = device not registered.
    Note, devices a required user input to accept or deny a registration
    request, so manual intervention is required.
    """
        if self.StartPrivetRegister(user=user):
            if no_action:
                print msg
            else:
                PromptUserAction(msg)
            if self.GetPrivetClaimToken(user=user,
                                        wait_for_user=wait_for_user):
                auth_token = self.auth_token if use_token else None
                if self.ConfirmRegistration(auth_token):
                    self.FinishPrivetRegister()
                    return True

        return False

    def GetDeviceCDDLocally(self):
        """Get device cdd and populate device object with the details via privet.

    Args:
      device_id: string, Cloud Print device id.
    Returns:
      boolean: True = cdd details populated, False = cdd details not populated.
    """
        r = self.transport.HTTPGet(self.privet_url['capabilities'],
                                   headers=self.headers)
        if r is None:
            return False
        response = r.json()

        if 'printer' in response:
            self.cdd['caps'] = {}
            for k in response['printer']:
                self.cdd['caps'][k] = response['printer'][k]
            self.supported_types = [
                type['content_type']
                for type in self.cdd['caps']['supported_content_type']
            ]
            return True
        else:
            self.logger.error('Could not find printers in cdd.')
        return False

    def __parseDeviceDetails(self, printer):
        """Parse through the printer device object and extract information

        Args:
          printer: dict, object containing printer device details."""
        for k in printer:
            if k == 'name':
                self.name = printer[k]
            elif k == 'connectionStatus':
                self.status = printer[k]
            elif k == 'id':
                self.dev_id = printer[k]
            else:
                self.details[k] = printer[k]

    def GetDeviceDetails(self):
        """Get the device details from our management page through the cloud.

    Returns:
      boolean: True = device populated, False = errors.

    This will populate a Device object with device name, status, state messages,
    and device details.
    """
        response = self.gcp.Search(self.name)
        if not response['printers']:
            print(
                '%s not found as registered printer under the /search gcp api'
                % self.name)
            print 'Update PRINTER["NAME"] in _config.py if misconfigured'
            full_response = self.gcp.Search()
            print "Below is the list of registered printers:"
            for printer in full_response['printers']:
                print printer['name']
            raise

        self.__parseDeviceDetails(response['printers'][0])
        if self.dev_id:
            if self.GetDeviceCDD(self.dev_id):
                self.supported_types = [
                    type['content_type']
                    for type in self.cdd['caps']['supported_content_type']
                ]
                return True

        return False

    def GetDeviceCDD(self, device_id):
        """Get device cdd and populate device object with the details via the cloud.

    Args:
      device_id: string, Cloud Print device id.
    Returns:
      boolean: True = cdd details populated, False = cdd details not populated.
    """
        info = self.gcp.Printer(device_id)
        if 'printers' in info:
            self.__parseCDD(info['printers'][0])
            if ('num_issues' in self.cdd['uiState']
                    and self.cdd['uiState']['num_issues'] > 0):
                self.error_state = True
            else:
                self.error_state = False
            return True
        else:
            self.logger.error('Could not find printers in cdd.')
        return False

    def __parseCDD(self, printer):
        """Parse the CDD json string into a logical dictionary.

    Args:
      printer: formatted data from /printer interface.
    Returns:
      boolean: True = CDD parsed, False = CDD not parsed.
    """
        for k in printer:
            if k == 'capabilities':
                self.cdd['caps'] = {}
            else:
                self.cdd[k] = printer[k]

        for k in printer['capabilities']['printer']:
            self.cdd['caps'][k] = printer['capabilities']['printer'][k]
        return True

    def CancelRegistration(self):
        """Cancel Privet Registration that is in progress.

    Returns:
      return code from HTTP request.
    """
        self.logger.debug('Sending request to cancel Privet Registration.')
        url = self.privet_url['register']['cancel']
        params = {'user': Constants.USER['EMAIL']}
        r = self.transport.HTTPPost(url, headers=self.headers, params=params)

        if r is None:
            raise

        Sleep('REG_CANCEL')

        return r.status_code

    def StartPrivetRegister(self, user=Constants.USER['EMAIL']):
        """Start a device registration using the Privet protocol.

    Returns:
      boolean: True = success, False = errors.
    """

        self.logger.debug('Registering device %s with Privet', self.ipv4)
        url = self.privet_url['register']['start']
        params = {'user': user}
        r = self.transport.HTTPPost(url, headers=self.headers, params=params)

        if r is None:
            return False

        return r.status_code == requests.codes.ok

    def GetPrivetClaimToken(self,
                            user=Constants.USER['EMAIL'],
                            wait_for_user=True):
        """Wait for user interaction with the Printer's UI and get a
       Privet Claim Token.
       Raises EnvironmentError if the printer keeps returning
       'pending_user_action'
    Args:
      user: string, email address to register under.
      wait_for_user: boolean, True if user is expected to interact with printer

    Returns:
      boolean: True = success, False = errors.
    """
        print(
            'Waiting up to 60 seconds for printer UI interaction '
            'then getting Privet Claim Token.')
        t_end = time.time() + 60
        while time.time() < t_end:
            url = self.privet_url['register']['getClaimToken']
            params = {'user': user}
            r = self.transport.HTTPPost(url,
                                        headers=self.headers,
                                        params=params)

            if r is None:
                raise
            response = r.json()

            if 'token' in response:
                self.claim_token = response['token']
                self.automated_claim_url = response['automated_claim_url']
                self.claim_url = response['claim_url']
                print 'Successfully got Claim Token for %s' % user
                return True

            if 'error' in response:
                if response['error'] == 'pending_user_action':
                    if not wait_for_user:
                        # Should not return 'pending_user_action' when printer is not
                        # waiting for user interaction
                        print(
                            "ERROR: getClaimToken() should not return "
                            "'pending_user_action when user input is not expected'"
                        )
                        raise EnvironmentError
                else:
                    return False
            # Keep polling for user interaction at a configurable interval
            time.sleep(Constants.SLEEP['POLL'])

        print 'GetPrivetClaimToken() timed out from waiting for printer interaction'
        return False

    def SendClaimToken(self, auth_token=None):
        """Send a claim token to the Cloud Print service.

    Args:
      auth_token: string, auth token of user registering printer.
    Returns:
      boolean: True = success, False = errors.
    """
        if not auth_token:
            auth_token = self.auth_token
        if not self.claim_token:
            self.logger.error('Error: device does not have claim token.')
            self.logger.error(
                'Cannot send empty token to Cloud Print Service.')
            return False
        if not self.automated_claim_url:
            self.logger.error('Error: expected automated_claim_url.')
            self.logger.error('Aborting SendClaimToken()')
            return False

        url = self.automated_claim_url
        params = {'user': Constants.USER['EMAIL']}
        headers = {'Authorization': 'Bearer %s' % auth_token}
        r = self.transport.HTTPPost(url, headers=headers, params=params)

        if r is None:
            return False

        if r.status_code == requests.codes.ok and r.json()['success']:
            return True

        return False

    def ConfirmRegistration(self, auth_token):
        """Register printer with GCP Service using claim token.

    Returns:
      boolean: True = printer registered, False = printer not registered.
    This method should only be called once self.claim_token is populated.
    """
        if not self.claim_token:
            self.logger.error('No claim token has been  set yet.')
            self.logger.error('Execute GetClaimToken() before this method.')
            return False
        url = '%s/confirm?token=%s' % (Constants.GCP['MGT'], self.claim_token)
        params = {'user': Constants.USER['EMAIL']}
        headers = self.headers
        headers['Authorization'] = 'Bearer %s' % auth_token
        r = self.transport.HTTPPost(url, headers=headers, params=params)

        if r is None:
            return False

        if r.status_code == requests.codes.ok and r.json()['success']:
            return True
        return False

    def FinishPrivetRegister(self):
        """Complete printer registration using Privet.

    Returns:
      boolean: True = success, False = errors.
    """

        self.logger.debug('Finishing printer registration.')
        url = self.privet_url['register']['complete']
        params = {'user': Constants.USER['EMAIL']}
        r = self.transport.HTTPPost(url, headers=self.headers, params=params)

        if r is None:
            return False

        # Add the device id from the Cloud Print Service.
        try:
            info = r.json()
        except ValueError:
            self.logger.info('No JSON object in response')
        else:
            if 'device_id' in info:
                self.dev_id = info['device_id']
                self.logger.debug('Registered with device id: %s', self.dev_id)
        return r.status_code == requests.codes.ok

    def UnRegister(self, auth_token):
        """Remove device from Google Cloud Service.
    Args:
      auth_token: string, auth token of device owner.
    Returns:
      boolean: True = success, False = errors.
    """
        if self.dev_id:
            delete_url = '%s/delete?printerid=%s' % (Constants.GCP['MGT'],
                                                     self.dev_id)
            headers = {'Authorization': 'Bearer %s' % auth_token}
            r = self.transport.HTTPPost(delete_url, headers=headers)
        else:
            self.logger.warning('Cannot delete device, not registered.')
            return False

        if r is None:
            return False

        if r.status_code == requests.codes.ok and r.json()['success']:
            self.logger.debug('Successfully deleted printer from service.')
            self.dev_id = None
            return True

        self.logger.error('Unable to delete printer from service.')
        return False

    def LocalPrint(self, title, content, cjt, content_type):
        """Submit a local print job to the printer

        Args:
          title: string, title of the print job
          content: string, url or absolute filepath of the item to print.
          cjt: CloudJobTicket, object that defines the options of the print job
          content_type: string, MIME type of the print data
        Returns:
          int, the job id of the local print job that succeeded, else None
        """
        print '\nWait for idle state before starting a local print job'
        success = self.WaitForPrinterState('idle')

        if not success:
            print 'Idle state not observed\n'
            return None

        job_id = self.CreateJob(cjt)
        if job_id is None:
            print 'Error creating a local print job.\n'
            return None

        output = self.SubmitDoc(job_id, title, content, content_type)
        if output is None:
            # Cancel the job creation to get back to a normal state
            self.CancelJob(job_id)
            print 'Error printing a local print job.'
            print(
                'Printer may be in an unstable state if the job isn\'t cancelled '
                'correctly, may need to reboot printer')

        return output

    def CreateJob(self, cjt=None):
        """First step required to submit a local print job.
       Keep trying to obtain the job id for 60 seconds if the printer returns
       busy status

        Args:
          cjt: CloudJobTicket, object that defines the options of the print job
        Returns:
          string, the newly created job_id if successful, else None
        """

        if cjt is None:
            cjt = {}
        else:
            cjt = cjt.val

        url = self.privet_url['createjob']

        print 'Attempt to get a local job id for up to 30 seconds'
        t_end = time.time() + 30

        while time.time() < t_end:
            r = self.transport.HTTPPost(url,
                                        data=dumps(cjt),
                                        headers=self.headers)

            if r is None or requests.codes.ok != r.status_code:
                return None

            res = r.json()

            if 'job_id' not in res:
                if 'error' in res and 'printer_busy' in res['error'].lower():
                    print(
                        'Printer is still busy, will try again in %s second(s)'
                        % Constants.SLEEP['POLL'])
                    Sleep('POLL')
                else:
                    print 'Error: ', res['error']
                    return None
            else:
                print 'Got a job id\n'
                return res['job_id']
        return None

    def SubmitDoc(self, job_id, title, content, content_type):
        """Second step for printing locally, submit a local print job to the printer

        Args:
          job_id: string, local job id that was returned by /createjob
          title: string, title of the print job
          content: string, url or absolute filepath of the item to print.
          content_type: string, MIME type of the print data
        Returns:
          int, the job id of the print job if successful, else None
            """
        with open(content, 'rb') as f:
            content = f.read()

        url = (self.privet_url['submitdoc'] + '?job_id=%s&job_name=%s' %
               (job_id, title))

        if content_type not in self.supported_types:
            print(
                'This printer does not support the following content type: %s'
                % (content_type))
            print 'List of supported types are: ', self.supported_types
            return None

        headers = self.headers  # Get X-Privet_Token
        headers['Content-Type'] = content_type

        r = self.transport.HTTPPost(url, data=content, headers=headers)

        if r is None:
            return None

        res = r.json()

        return job_id if 'job_id' in res else None

    def JobState(self, job_id):
        """Optional Api that printers can implement to track job states

        Args:
          job_id: string, local job id

        Returns:
          dict, The response of the API call if succeeded, else None
        """
        url = self.privet_url['jobstate'] + '?job_id=%s' % job_id

        r = self.transport.HTTPGet(url, headers=self.headers)

        if r is None or requests.codes.ok != r.status_code:
            return None

        return r.json()

    def Info(self):
        """Make call to the privet/info API to get the latest printer info

      Returns:
        dict, the info object if successful, else None
        """
        response = self.transport.HTTPGet(self.privet_url['info'],
                                          headers=self.privet.headers_empty)
        if response is None:
            return None

        try:
            info = response.json()
        except ValueError:
            self.logger.error(
                'Privet Info response does not contain JSON object')
            self.logger.debug('HTTP device return code: %s',
                              response.status_code)
            self.logger.debug('HTTP Headers:  ')
            for key in response.headers:
                self.logger.debug('%s: %s', key, response.headers[key])
            return None
        else:
            return info

    def WaitForPrinterState(self,
                            state,
                            timeout=Constants.TIMEOUT['PRINTER_STATUS']):
        """Wait until the printer state becomes the specified status

        Args:
          state: string, printer state to wait for
          timeout: integer, number of seconds to wait.
        Returns:
          boolean, True if state is observed within timeout; otherwise, False.
        """
        print '[Configurable timeout] PRINTER_STATUS:'
        print('Waiting up to %s seconds for the printer to have status: %s' %
              (timeout, state))

        end = time.time() + timeout

        while time.time() < end:
            info = self.Info()
            if info is not None:
                if info['device_state'] == state:
                    print 'Device state observed to be: %s' % state
                    return True
            Sleep('POLL')

        return False

    def isPrinterRegistered(self):
        """Use the /privet/info interface to see if printer is registered

    Returns:
      boolean, True if registered, False if not registered,
               None if /privet/info failed
    """
        info = self.Info()
        if info is not None:
            return info['id'] and info['connection_state'] == 'online'
        return None

    def assertPrinterIsRegistered(self):
        """Raise exception if printer is unregistered"""
        if not self.isPrinterRegistered():
            print RedText('ERROR: Printer needs to be registered before this '
                          'suite runs')
            raise EnvironmentError

    def assertPrinterIsUnregistered(self):
        """Raise exception if printer is registered"""
        if self.isPrinterRegistered():
            print RedText(
                'ERROR: Printer needs to be unregistered before this '
                'suite runs')
            raise EnvironmentError

    def CancelJob(self, job_id):
        #TODO: Posting a mismatch job seems to cancel the created job,
        # find a better way to do this
        url = self.privet_url['submitdoc'] + '?job_id=%s' % (job_id)

        headers = self.headers
        headers['Content-Type'] = 'image/pwg-raster'

        self.transport.HTTPPost(url,
                                data=Constants.IMAGES['PDF13'],
                                headers=headers)
Exemplo n.º 8
0
class GCPService(object):
  """Send and receive network messages and communication."""

  def __init__(self, auth_token):
    """Get a reference to a logger object.

      Args:
      auth_token: the authentication token to use for GCP requests
    """
    self.auth_token = auth_token
    self.logger = _log.GetLogger('LogoCert')
    self.transport = Transport(self.logger)


  def VerifyNotNone(query):
    """Decorator to check that None is not returned.
       This keeps calling code cleaner

    Args:
      query: function we are wrapping.
    Returns:
      formatted data from query if valid, otherwise raise exception
    """
    def VerifyNotNone(self, *args, **kwargs):
      res = query(self, *args, **kwargs)
      if res is None:
        print '%s failed' % str(query)
        raise AssertionError
      return res

    return VerifyNotNone


  def HTTPGetQuery(query):
    """Decorator for various queries to GCP interfaces

    Args:
      query: function we are wrapping.
    Returns:
      formatted data from query to GCP Service Interface.
    """
    def GCPQuery(self, *args, **kwargs):
      url = query(self, *args, **kwargs)
      headers = {'Authorization': 'Bearer %s' % self.auth_token}
      res = self.transport.HTTPGet(url, headers=headers)

      response_dict = {}
      Extract(res.json(), response_dict)
      return response_dict

    return GCPQuery

  @VerifyNotNone
  def FetchRaster(self, job_id):
    """Get the data content belonging to a job_id in pwg-raster format
       Note: This only works for job_id's that are queued, or in_progress.
             This will not work for jobs that have finished

       Args:
          job_id: string, printer's id
       Returns:
         str, the content in pwg-raster format if successful, otherwise, None

         """
    url = '%s/download?id=%s&forcepwg=1' % (Constants.GCP['MGT'], job_id)
    headers = {'Authorization': 'Bearer %s' % self.auth_token}
    r = self.transport.HTTPGet(url, headers=headers)

    if r is None or requests.codes.ok != r.status_code:
      if r is None:
        print 'ERROR! Request to /download returned None type'
      elif r.status_code == 415:
        print ('GCP failed to provide raster file conversion, either supply '
               'your own raster files or capture the content via wireshark '
               'from manual printing the following from Chrome: testpage.png, '
               'rosemary.pdf, dna_overview.png')
      else:
        print ('ERROR! Bad HTTP status code received from /download: %s' %
               r.status_code)
      return None

    return r.content


  # Not decorated with @HTTPGetQuery since Submit() is an HTTP Post
  @VerifyNotNone
  def Submit(self, printer_id, content, title, cjt=None, is_url=False):
    """Submit a print job to the printer

        Args:
          printer_id: string, target printer to print from.
          content: string, url or absolute filepath of the item to print.
          title: string, title of the print job.
          cjt: CloudJobTicket, object that defines the options of the print job
          is_url: boolean, flag to identify between url's and files
        Returns:
          dictionary, response msg from the printer if successful;
                      otherwise, None
        """

    if cjt is None:
      cjt = {}
    else:
      cjt = cjt.val

    name = content

    if not is_url:
      name = basename(content)
      with open(content, 'rb') as f:
        content = f.read()

    if title is None:
      title = "LogoCert Testing: " + name

    content_type = 'url' if is_url else mimetypes.guess_type(name)[0]
    files = {"content": (name,content)}
    url = '%s/submit' % (Constants.GCP['MGT'])
    headers = {'Authorization': 'Bearer %s' % self.auth_token}

    data = {'printerid': printer_id,
            'title': title,
            'contentType': content_type,
            'ticket': dumps(cjt)}

    # Depending on network speed, large files may take a while to submit
    print 'Attempting to submit a job through GCP for up to 60 seconds'
    t_end = time.time()+60
    while time.time() < t_end:
      r = self.transport.HTTPPost(url, data=data, files=files, headers=headers)
      if r is None:
        print 'ERROR! HTTP POST to /submit returned None type'
        return None
      elif r.status_code == requests.codes.ok:
        # Success
        res = r.json()
        # TODO may have to fuzzy match here, print job added may not be standard
        res['success'] = (res['success'] and
                          'print job added' in res['message'].lower())

        if res['success']:
          print 'Job submitted successfully'
        else:
          print 'success: %s, msg: %s' % (res['success'], res['message'])
        return res
      else:
        # Try again if we get HTTP error code
        print 'Bad status code from Submit(): %s' % r.status_code
        if r.status_code == requests.codes.forbidden:
          # This should not happen, calling code should manage token refresh
          self.logger.info('Access token expired, need to refresh it.')
        print 'Trying again in %s sec(s)' % Constants.SLEEP['POLL']
        Sleep('POLL')
    # Continuously gotten HTTP error codes to fall out of the while loop
    return None

  # Not decorated with @HTTPGetQuery since Update() is an HTTP Post
  @VerifyNotNone
  def Update(self, printer_id, setting):
    """Update a cloud printer

        Args:
          printer_id: string, target printer to update.
          setting: dict, local settings structure that describes the fields
                         to update
        Returns:
          dictionary, response msg from the printer
        """
    url = '%s/update' % (Constants.GCP['MGT'])
    headers = {'Authorization': 'Bearer %s' % self.auth_token}

    data = {'printerid': printer_id,
            'local_settings': dumps(setting)}

    r = self.transport.HTTPPost(url, data=data, headers=headers)

    if r is None or requests.codes.ok != r.status_code:
      return False

    res = r.json()
    # TODO may have to fuzzy match here, print job added may not be a standard
    res['success'] = (res['success'] and
                      'printer updated successfully' in res['message'].lower())
    return res


  @VerifyNotNone
  @HTTPGetQuery
  def Delete(self, printer_id):
    """Delete a printer owned by a user.

    Args:
      printer_id: string, printerid of registered printer.
    Returns:
      url: string, url to delete printer.
    """
    url = '%s/delete?printerid=%s' % (Constants.GCP['MGT'], printer_id)

    return url

  @VerifyNotNone
  @HTTPGetQuery
  def DeleteJob(self, job_id):
    """Delete a job owned by user.

    Args:
      job_id: string, jobid of existing job owned by user.
    Returns:
      url: string, url to delete job.
    """
    url = '%s/deletejob?jobid=%s' % (Constants.GCP['MGT'], job_id)

    return url

  @VerifyNotNone
  @HTTPGetQuery
  def Jobs(self, printer_id=None, owner=None, job_title=None, status=None):
    """Get a list of print jobs which user has permission to view.

    Args:
      printer_id: string, filter jobs sent to this printer.
      owner: string, filter jobs submitted by this owner.
      job_title: string, filter jobs whose title or tags contain this string.
      status: string, filter jobs that match this status.
    Returns:
      string, url to be used by HTTPGetQuery method.
    Valid Job state strings are: QUEUED, IN_PROGRESS, DONE, ERROR, SUBMITTED,
    and HELD.
    """
    args = '?'
    url = '%s/jobs' % Constants.GCP['MGT']
    if printer_id:
      url += '?printerid=%s' % printer_id
      args = '&'
    if owner:
      url += '%sowner=%s' % (args, owner)
      args = '&'
    if status:
      url += '%sstatus=%s' % (args, status)
      args= '&'
    if job_title:
      url += '%sq=%s' % (args, job_title)

    return url

  @VerifyNotNone
  @HTTPGetQuery
  def List(self, proxy_id):
    """Execute the list interface and return printer fields.

    Args:
      proxy_id: string, proxy of printer.
    Returns:
      string: url to by used by HTTPGetQuery method.
    Note: the List interface returns the same information as the Search
    interface; therefore, use the Search interface unless you need a list
    or printers using the same proxy_id.
    """
    url = '%s/list?proxy=%s' % (Constants.GCP['MGT'], proxy_id)

    return url

  @VerifyNotNone
  @HTTPGetQuery
  def Printer(self, printer_id):
    """Execute the printer interface and return printer fields and capabilites.

    Args:
      printer_id: string, id of printer.
    Returns:
      string: url to be used by HTTPGetQuery method.
    """
    fields = 'connectionStatus,semanticState,uiState,queuedJobsCount'
    url = '%s/printer?printerid=%s&usecdd=True&extra_fields=%s' % (
        Constants.GCP['MGT'], printer_id, fields)

    return url

  @VerifyNotNone
  @HTTPGetQuery
  def Search(self, printer=None):
    """Search for printers owned by user.

    Args:
      printer: string, name or partial name of printer to search for.
    Returns:
      string: url to be used by HTTPGetQuery method.
    """
    url = '%s/search' % Constants.GCP['MGT']
    if printer:
      # replace all spaces with %20
      url += '?q=%s' % printer.replace(' ','%20')

    return url


  def __getJobFromList(self, job_list, job_id):
    """Find the specified job_id in a list of jobs

    Args:
      job_list: array, job objects.
      job_id: the job_id to look for
    Returns:
      object: the job object with the specified job_id
    """
    for entry in job_list:
      if entry['id'] == job_id:
        return entry
    return None

  def GetJobInfo(self, job_id, printer_id, owner=None, job_title=None):
      """Find the specified job_id in from the Job query result

          Args:
            job_id: string, id of the print job.
            printer_id: string, id of the printer
            owner: string, filter jobs submitted by this owner.
            job_title: string, filter jobs whose title or tags contain this str.
          Returns:
            object: the job object with the specified job_id
      """
      res = self.Jobs(printer_id=printer_id, owner=owner, job_title=job_title)
      job = self.__getJobFromList(res['jobs'], job_id)
      return job

  @VerifyNotNone
  def WaitJobStateNotIn(self, job_id, printer_id, job_state, timeout=60):
    """Wait until the job state is not the specified state.

    Args:
      job_id: string, id of the print job.
      printer_id: string, id of the printer
      job_state: string or list, job state(s) that should not be observed.
      timeout: integer, number of seconds to wait.
    Returns:
      string, current job.

    """
    print ('Waiting up to %s seconds for the job to not have any of the '
           'following job state(s): %s\n' % (timeout, job_state))

    end = time.time() + timeout

    while time.time() < end:
      job = self.GetJobInfo(job_id, printer_id)

      if job is not None:
        if job['semanticState']['state']['type'] not in job_state:
          return job

      Sleep('POLL')

    return None

  @VerifyNotNone
  def WaitJobStateIn(self, job_id, printer_id, job_state, timeout=60):
    """Wait until the job state becomes the specified state(s)

    Args:
      job_id: string, id of the print job.
      printer_id: string, id of the printer
      job_state: string or list, job state(s) to wait for.
      timeout: integer, number of seconds to wait.
    Returns:
      dict, current job.

    """
    print ('Waiting up to %s seconds for the job to have one of the following '
           'job state(s): %s\n' % (timeout, job_state))

    end = time.time() + timeout

    while time.time() < end:
      job = self.GetJobInfo(job_id, printer_id)

      if job is not None:
        if job['semanticState']['state']['type'] in job_state:
          return job

      Sleep('POLL')

    return None


  def WaitForUpdate(self, dev_id, key, expected_value,
                    timeout=Constants.TIMEOUT['GCP_UPDATE']):
    '''Wait for the printer's local_settings attribute matches an expected value

      Args:
        dev_id: string, id of the printer.
        key: string, the local_settings attribute to poll for.
        expected_value: int or boolean, the expected value of the attribute.
        timeout: integer, number of seconds to wait.
      Returns:
        boolean, True if expected value is observed, otherwise False
    '''
    print '[Configurable timeout] GCP_UPDATE:'
    print ('Waiting up to %s seconds for printer to accept pending settings' %
           timeout)

    end = time.time() + timeout

    while time.time() < end:
      # Continue to use the /Update to access the current local settings
      try:
        res = self.Update(dev_id,{})
      except AssertionError:
        print 'GCP Update call failed'
        return False
      else:
        if key not in res['printer']['local_settings']['current']:
          print 'ERROR: "%s" does not exist in local_settings' % key
          return False
        cur_val = res['printer']['local_settings']['current'][key]
        if expected_value == cur_val:
          return True
      Sleep('POLL')
    return False


  def WaitLocalJobExist(self, printer_id, job_title, timeout=60):
    """Wait until the local print job is present in /job api.

    Args:
      printer_id: string, id of the printer
      job_title: string, title of the print job.
      timeout: integer, number of seconds to wait.
    Returns:
      boolean, True if job exists.

    """
    print ('Waiting up to %s seconds for the local print job to be reported to '
           'GCP servers\n' % timeout)

    end = time.time() + timeout

    while time.time() < end:
      res = self.Jobs(printer_id=printer_id, job_title=job_title)
      if res['jobsCount'] > 0:
        return True

      Sleep('POLL')

    return False
Exemplo n.º 9
0
class Oauth2(object):
    """Send and receive network messages and communication."""
    def __init__(self, logger):
        """Get a reference to a logger object and JsonParser.

    Args:
        logger: initialized logger object.
    """
        self.logger = logger
        self.storage = Storage(Constants.AUTH['CRED_FILE'])
        self.transport = Transport(self.logger)

    def GetTokens(self):
        """Retrieve credentials."""
        if 'REFRESH' in Constants.AUTH:
            self.RefreshToken()
        else:
            creds = self.storage.get()
            if creds:
                Constants.AUTH['REFRESH'] = creds.refresh_token
                Constants.AUTH['ACCESS'] = creds.access_token
                self.RefreshToken()
            else:
                self.getNewTokens()

    def RefreshToken(self):
        """Get a new access token with an existing refresh token."""
        response = self.refreshTokenImpl()
        # If there is an error in the response, it means the current access token
        # has not yet expired.
        if 'access_token' in response:
            self.logger.info('Got new access token.')
            Constants.AUTH['ACCESS'] = response['access_token']
        else:
            self.logger.info('Using current access token.')

    def getNewTokens(self):
        """Get all new tokens for this user account.

    This process is described in detail here:
    https://developers.google.com/api-client-library/python/guide/aaa_oauth

    If there is a problem with the automation authorizing access, then you
    may need to manually access the permit_url while logged in as the test user
    you are using for this automation.
    """
        flow = OAuth2WebServerFlow(
            client_id=Constants.USER['CLIENT_ID'],
            client_secret=Constants.USER['CLIENT_SECRET'],
            login_hint=Constants.USER['EMAIL'],
            redirect_uri=Constants.AUTH['REDIRECT'],
            scope=Constants.AUTH['SCOPE'],
            user_agent=Constants.AUTH['USER_AGENT'],
            approval_prompt='force')

        http = httplib2.Http()
        flags = argparser.parse_args(args=[])

        # retrieves creds and stores it into storage
        creds = run_flow(flow, self.storage, flags=flags, http=http)

        if creds:
            Constants.AUTH['REFRESH'] = creds.refresh_token
            Constants.AUTH['ACCESS'] = creds.access_token
            self.RefreshToken()
        else:
            self.logger.error('Error getting authorization code.')

    def refreshTokenImpl(self):
        """Obtains a new token given a refresh token.

    Returns:
      The decoded response from the Google Accounts server, as a dict. Expected
      fields include 'access_token', 'expires_in', and 'refresh_token'.
    Before you execute this function make sure you've added your account's
    refresh token. This is done automatically when the LogoCert class is
    initialized.
    """
        params = {}
        params['client_id'] = Constants.USER['CLIENT_ID']
        params['client_secret'] = Constants.USER['CLIENT_SECRET']
        params['refresh_token'] = Constants.AUTH['REFRESH']
        params['grant_type'] = 'refresh_token'

        headers = {
            'User-Agent': 'LogoCert Client',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'text/html, */*',
        }

        request_url = Constants.OAUTH_TOKEN

        res = self.transport.HTTPPost(request_url,
                                      headers=headers,
                                      params=params)

        return res.json()
Exemplo n.º 10
0
class Device(object):
    """The basic device object."""
    def __init__(self, logger, chromedriver, model=None, privet_port=None):
        """Initialize a device object.

    Args:
      logger: initialized logger object.
      chromedriver: initialized chromedriver object.
      model: string, unique model or name of device.
      privet_port: integer, tcp port devices uses for Privet protocol.
    """
        if model:
            self.model = model
        else:
            self.model = Constants.PRINTER['MODEL']
        self.logger = logger
        self.cd = chromedriver
        self.cloudprintmgr = CloudPrintMgr(logger, chromedriver)
        self.ipv4 = Constants.PRINTER['IP']
        if privet_port:
            self.port = privet_port
        else:
            self.port = Constants.PRINTER['PORT']
        self.name = Constants.PRINTER['NAME']
        self.status = None
        self.messages = {}
        self.details = {}
        self.error_state = False
        self.warning_state = False
        self.cdd = {}
        self.info = None

        self.url = 'http://%s:%s' % (self.ipv4, self.port)
        self.logger.info('Device URL: %s', self.url)
        self.transport = Transport(logger)
        self.jparser = JsonParser(logger)
        self.headers = None
        self.privet = Privet(logger)
        self.privet_url = self.privet.SetPrivetUrls(self.ipv4, self.port)
        self.GetPrivetInfo()

    def GetPrivetInfo(self):
        self.privet_info = {}
        response = self.transport.HTTPReq(self.privet_url['info'],
                                          headers=self.privet.headers_empty)
        info = self.jparser.Read(response['data'])
        if info['json']:
            for key in info:
                self.privet_info[key] = info[key]
                self.logger.debug('Privet Key: %s', key)
                self.logger.debug('Value: %s', info[key])
                self.logger.debug('--------------------------')
            if 'x-privet-token' in info:
                self.headers = {'X-Privet-Token': str(info['x-privet-token'])}
        else:
            if response['code']:
                self.logger.info('HTTP device return code: %s',
                                 response['code'])
            if response['headers']:
                self.logger.debug('HTTP Headers:  ')
                for key in response['headers']:
                    self.logger.debug('%s: %s', key, response['headers'][key])
            if response['data']:
                self.logger.info('Data from response: %s', response['data'])

    def GetDeviceDetails(self):
        """Get the device details from our management page.

    This will populate a Device object with device name, status, state messages,
    and device details.
    """

        RETRY_COUNT = 3

        self.error_state = None
        self.warning_state = None
        self.status = None
        self.messages = None
        self.details = None

        for i in range(-1, RETRY_COUNT):
            self.cd.page_id = None
            if not self.error_state:
                self.error_state = self.cloudprintmgr.GetPrinterErrorState(
                    self.name)
            if not self.warning_state:
                self.warning_state = self.cloudprintmgr.GetPrinterWarningState(
                    self.name)
            if not self.status:
                self.status = self.cloudprintmgr.GetPrinterState(self.name)
            if not self.messages:
                self.messages = self.cloudprintmgr.GetPrinterStateMessages(
                    self.name)
            if not self.details:
                self.details = self.cloudprintmgr.GetPrinterDetails(self.name)

    def GetDeviceCDD(self, device_id):
        """Get device cdd and populate device object with the details.

    Args:
      device_id: string, Cloud Print device id.
    Returns:
      boolean: True = cdd details populated, False = cdd details not populated.
    """
        self.cd.Get(Constants.GCP['SIMULATE'])

        printer_lookup = self.cd.FindID('printer_printerid')
        if not printer_lookup:
            return False
        if not self.cd.SendKeys(device_id, printer_lookup):
            return False
        printer_submit = self.cd.FindID('printer_submit')
        if not self.cd.ClickElement(printer_submit):
            return False
        printer_info = self.cd.FindXPath('html')
        if not printer_info:
            return False
        self.info = printer_info.text
        self.ParseCDD()
        return True

    def ParseCDD(self):
        """Parse the CDD json string into a logical dictionary.

    Returns:
      boolean: True = CDD parsed, False = CDD not parsed.
    """

        cdd = {}
        if self.info:
            cdd = json.loads(self.info)
        else:
            self.logger.warning('Device info is empty.')
            return False
        if 'printers' in cdd:
            for k in cdd['printers'][0]:
                if k == 'capabilities':
                    self.cdd['caps'] = {}
                else:
                    self.cdd[k] = cdd['printers'][0][k]
        else:
            self.logger.error('Could not find printers in cdd.')
            return False
        for k in cdd['printers'][0]['capabilities']['printer']:
            self.cdd['caps'][k] = cdd['printers'][0]['capabilities'][
                'printer'][k]
        return True

    def CancelRegistration(self):
        """Cancel Privet Registration that is in progress.

    Returns:
      return code from HTTP request.
    """
        cancel_url = self.privet_url['register']['cancel']
        self.logger.debug('Sending request to cancel Privet Registration.')
        response = self.transport.HTTPReq(cancel_url,
                                          data='',
                                          headers=self.headers,
                                          user=Constants.USER['EMAIL'])
        return response['code']

    def StartPrivetRegister(self):
        """Start a device registration using the Privet protocol.

    Returns:
      boolean: True = success, False = errors.
    """

        self.logger.debug('Registering device %s with Privet', self.ipv4)
        response = self.transport.HTTPReq(self.privet_url['register']['start'],
                                          data='',
                                          headers=self.headers,
                                          user=Constants.USER['EMAIL'])
        return self.transport.LogData(response)

    def GetPrivetClaimToken(self):
        """Attempt to get a Privet Claim Token.

    Returns:
      boolean: True = success, False = errors.
    """
        self.logger.debug('Getting Privet Claim Token.')
        counter = 0
        max_cycles = 5  # Don't loop more than this number of times.
        while counter < max_cycles:
            response = self.transport.HTTPReq(
                self.privet_url['register']['getClaimToken'],
                data='',
                headers=self.headers,
                user=Constants.USER['EMAIL'])
            self.transport.LogData(response)
            if 'token' in response['data']:
                self.claim_token = self.jparser.GetValue(response['data'],
                                                         key='token')
                self.automated_claim_url = self.jparser.GetValue(
                    response['data'], key='automated_claim_url')
                self.claim_url = self.jparser.GetValue(response['data'],
                                                       key='claim_url')
                return True

            if 'error' in response['data']:
                self.logger.warning(response['data'])
                if 'pending_user_action' in response['data']:
                    counter += 1
                else:
                    return False

        return False  # If here, means unexpected condition, so return False.

    def SendClaimToken(self, auth_token):
        """Send a claim token to the Cloud Print service.

    Args:
      auth_token: string, auth token of user registering printer.
    Returns:
      boolean: True = success, False = errors.
    """
        if not self.claim_token:
            self.logger.error('Error: device does not have claim token.')
            self.logger.error(
                'Cannot send empty token to Cloud Print Service.')
            return False
        if not self.automated_claim_url:
            self.logger.error('Error: expected automated_claim_url.')
            self.logger.error('Aborting SendClaimToken()')
            return False
        response = self.transport.HTTPReq(self.automated_claim_url,
                                          auth_token=auth_token,
                                          data='',
                                          user=Constants.USER['EMAIL'])
        self.transport.LogData(response)
        info = self.jparser.Read(response['data'])
        if info['json']:
            if info['success']:
                return True
            else:
                return False
        else:
            return False

    def FinishPrivetRegister(self):
        """Complete printer registration using Privet.

    Returns:
      boolean: True = success, False = errors.
    """

        self.logger.debug('Finishing printer registration.')
        response = self.transport.HTTPReq(
            self.privet_url['register']['complete'],
            data='',
            headers=self.headers,
            user=Constants.USER['EMAIL'])
        # Add the device id from the Cloud Print Service.
        info = self.jparser.Read(response['data'])
        if info['json']:
            for k in info:
                if 'device_id' in k:
                    self.id = info[k]
                    self.logger.debug('Registered with device id: %s', self.id)
        return self.transport.LogData(response)

    def UnRegister(self, auth_token):
        """Remove device from Google Cloud Service.

    Args:
      auth_token: string, auth token of device owner.
    Returns:
      boolean: True = success, False = errors.
    """
        if self.id:
            delete_url = '%s/delete?printerid=%s' % (
                Constants.AUTH['URL']['GCP'], self.id)
            response = self.transport.HTTPReq(delete_url,
                                              auth_token=auth_token,
                                              data='')
        else:
            self.logger.warning('Cannot delete device, not registered.')
            return False

        result = self.jparser.Validate(response['data'])
        if result:
            self.logger.debug('Successfully deleted printer from service.')
            self.id = None
            return True
        else:
            self.logger.error('Unable to delete printer from service.')
            return False

    def GetPrinterInfo(self, auth_token):
        """Get the printer capabilities stored on the service.

    Args:
      auth_token: string, auth token of device owner.
    Returns:
      boolean: True = success, False = errors.
    """
        if self.id:
            printer_url = '%s/printer?printerid=%s&usecdd=True' % (
                Constants.AUTH['URL']['GCP'], self.id)
            response = self.transport.HTTPReq(printer_url,
                                              auth_token=auth_token)
        else:
            self.logger.warning(
                'Cannot get printer info, device not registered.')
            return False

        info = self.jparser.Read(response['data'])
        Extract(info, self.info)
        for k, v in self.info.iteritems():
            self.logger.debug('%s: %s', k, v)
            self.logger.debug('=============================================')
        return True