docs: def perspective_commandline(self, op, bb_username, bb_password, ids, info):
          """
          This performs the requested operations from the `buildbot user`
          call by calling the proper buildbot.db.users methods based on
          the operation. It yields a deferred instance with the results
          from the database methods.
  
          @param op: operation to perform (add, remove, update, get)
          @type op: string
  
          @param bb_username: username portion of auth credentials
          @type bb_username: string
  
          @param bb_password: hashed password portion of auth credentials
          @type bb_password: hashed string
  
          @param ids: user identifiers used to find existing users
          @type ids: list of strings or None
  
          @param info: type/value pairs for each user that will be added
                       or updated in the database
          @type info: list of dictionaries or None
  
          @returns: results from db.users methods via deferred
          """
          log.msg("perspective_commandline called")
          results = []
  
          # pylint: disable=too-many-nested-blocks
          if ids:
              for user in ids:
                  # get identifier, guaranteed to be in user from checks
                  # done in C{scripts.runner}
                  uid = yield self.master.db.users.identifierToUid(
                      identifier=user)
  
                  result = None
                  if op == 'remove':
                      if uid:
                          yield self.master.db.users.removeUser(uid)
                          result = user
                      else:
                          log.msg("Unable to find uid for identifier %s" % user)
                  elif op == 'get':
                      if uid:
                          result = yield self.master.db.users.getUser(uid)
                      else:
                          log.msg("Unable to find uid for identifier %s" % user)
  
                  results.append(result)
          else:
              for user in info:
                  # get identifier, guaranteed to be in user from checks
                  # done in C{scripts.runner}
                  ident = user.pop('identifier')
                  uid = yield self.master.db.users.identifierToUid(
                      identifier=ident)
  
                  # if only an identifier was in user, we're updating only
                  # the bb_username and bb_password.
                  if not user:
                      if uid:
                          result = yield self.master.db.users.updateUser(
                              uid=uid,
                              identifier=ident,
                              bb_username=bb_username,
                              bb_password=bb_password)
                          results.append(ident)
                      else:
                          log.msg("Unable to find uid for identifier %s"
                                  % user)
                  else:
                      # when adding, we update the user after the first attr
                      once_through = False
                      for attr in user:
                          result = None
                          if op == 'update' or once_through:
                              if uid:
                                  result = yield self.master.db.users.updateUser(
                                      uid=uid,
                                      identifier=ident,
                                      bb_username=bb_username,
                                      bb_password=bb_password,
                                      attr_type=attr,
                                      attr_data=user[attr])
                              else:
                                  log.msg("Unable to find uid for identifier %s"
                                          % user)
                          elif op == 'add':
                              result = yield self.master.db.users.findUserByAttr(
                                  identifier=ident,
                                  attr_type=attr,
                                  attr_data=user[attr])
                              once_through = True
                          results.append(ident)
  
                          # result is None from updateUser calls
                          if result:
                              results.append(result)
                              uid = result
          results = self.formatResults(op, results)
          return results