예제 #1
0
파일: ws.py 프로젝트: LiaoWenyun/cadctools
 def check_status(self, response):
     """
     Check the response status. Maps the application related requests
     error status into Exceptions and raises the others
     :param response: response
     :return:
     """
     try:
         response.raise_for_status()
     except requests.HTTPError as e:
         if e.response.status_code == requests.codes.not_found:
             raise exceptions.NotFoundException(orig_exception=e)
         elif e.response.status_code == requests.codes.unauthorized:
             raise exceptions.UnauthorizedException(orig_exception=e)
         elif e.response.status_code == requests.codes.forbidden:
             raise exceptions.ForbiddenException(orig_exception=e)
         elif e.response.status_code == requests.codes.bad_request:
             raise exceptions.BadRequestException(orig_exception=e)
         elif e.response.status_code == requests.codes.conflict:
             raise exceptions.AlreadyExistsException(orig_exception=e)
         elif e.response.status_code == \
                 requests.codes.internal_server_error:
             raise exceptions.InternalServerException(orig_exception=e)
         elif e.response.status_code == \
                 requests.codes.request_entity_too_large:
             raise exceptions.ByteLimitException(orig_exception=e)
         elif self.retry and e.response.status_code in self.retry_errors:
             raise e
         else:
             raise exceptions.UnexpectedException(orig_exception=e)
예제 #2
0
파일: ws.py 프로젝트: LiaoWenyun/cadctools
    def __init__(self, resource_id, subject, agent, retry=True, host=None,
                 session_headers=None):
        """
        Client constructor
        :param resource_id -- ID of the resource being accessed (URI format)
        as it appears in the registry.
        :param subject -- The subject that is using the service
        :type subject: cadcutil.auth.Subject
        :param agent -- Name of the agent (application) that accesses the
        service and its version, e.g. foo/1.0.2
        :type agent: Subject
        :param retry -- True if the client retries on transient errors False
        otherwise
        :param host -- override the name of the host the service is running on
        (for testing purposes)
        :param session_headers -- Headers used throughout the session -
        dictionary format expected.
        """

        self.logger = logging.getLogger('BaseWsClient')
        logging.getLogger('BaseWsClient').addHandler(logging.NullHandler())

        if resource_id is None:
            raise ValueError('No resource ID provided')
        if agent is None or not agent:
            raise ValueError('agent is None or empty string')

        self._session = None
        self.subject = subject
        self.resource_id = resource_id
        self.retry = retry
        self.session_headers = session_headers

        # agent is / delimited key value pairs, separated by a space,
        # containing the application name and version,
        # plus the name and version of application libraries.
        # eg: foo/1.0.2 foo-lib/1.2.3
        self.agent = agent

        # Get the package name and version, plus any imported libraries.
        self.package_info = "cadcutils/{} requests/{}".format(
            cadctools_version.version,
            requests.__version__)
        self.python_info = "{}/{}".format(platform.python_implementation(),
                                          platform.python_version())
        self.system_info = "{}/{}".format(platform.system(),
                                          platform.version())
        o_s = sys.platform
        if o_s.lower().startswith('linux'):
            distname, version, osid = distro.linux_distribution()
            self.os_info = "{} {}".format(distname, version)
        elif o_s == "darwin":
            release, version, machine = platform.mac_ver()
            self.os_info = "Mac OS X {}".format(release)
        elif o_s.lower().startswith("win32"):
            release, version, csd, ptype = platform.win32_ver()
            self.os_info = "{} {}".format(release, version)

        # build the corresponding capabilities instance
        self.caps = WsCapabilities(self, host)
        self._host = host
        if resource_id.startswith('http'):
            self._host = resource_id
        if self._host is None:
            base_url = self.caps.get_access_url(SERVICE_AVAILABILITY_ID)
            self._host = urlparse(base_url).hostname

        # Clients should add entries to this dict for specialized
        # conversion of HTTP error codes into particular exceptions.
        #
        # Use this form to include a search string in the response to
        # handle multiple possibilities for a single HTTP code.
        #     XXX : {'SEARCHSTRING1' : exceptionInstance1,
        #            'SEARCHSTRING2' : exceptionInstance2}
        #
        # Otherwise provide a simple HTTP code -> exception mapping
        #     XXX : exceptionInstance
        #
        # The actual conversion is performed by get_exception()
        self._HTTP_STATUS_CODE_EXCEPTIONS = {
            401: exceptions.UnauthorizedException()}
예제 #3
0
    def put_file(self,
                 archive,
                 src_file,
                 archive_stream=None,
                 mime_type=None,
                 mime_encoding=None,
                 md5_check=True,
                 input_name=None):
        """
        Puts a file into the archive storage
        :param archive: name of the archive
        :param src_file: location of the source file
        :param archive_stream: specific archive stream
        :param mime_type: file mime type
        :param mime_encoding: file mime encoding
        :param md5_check: if True, calculate the md5sum before sending the file
        Server will fail if it receives a corrupted file.
        :param input_name: name to use in the archive overriding the actual
        file name.
        """
        if not archive:
            raise AttributeError('No archive specified')

        # We actually raise an exception here since the web
        # service will normally respond with a 200 for an
        # anonymous put, though not provide any endpoints.
        if self._data_client.subject.anon:
            raise exceptions.UnauthorizedException(
                'Must be authenticated to put data')

        self.logger.debug('PUT {}/{}'.format(archive, src_file))

        headers = {}
        if md5_check:
            # calculate the md5sum
            md5sum = self._get_md5sum(src_file)
            headers['Content-MD5'] = md5sum
            logger.debug('Set Content-MD5: {}'.format(md5sum))

        if archive_stream is not None:
            headers[ARCHIVE_STREAM_HTTP_HEADER] = str(archive_stream)
        if mime_type is not None:
            mtype = mime_type
        elif MAGIC_WARN:
            mtype = None
            logger.warning(MAGIC_WARN)
        else:
            m = magic.Magic(mime=True)
            mtype = m.from_file(os.path.realpath(src_file))
        if mtype is not None:
            headers['Content-Type'] = mtype
            logger.debug('Set MIME type: {}'.format(mtype))

        if mime_encoding:
            mencoding = mime_encoding
        elif MAGIC_WARN:
            mencoding = None
            if mtype:
                logger.warning(MAGIC_WARN)
        else:
            m = magic.Magic(mime_encoding=True)
            mencoding = m.from_file(os.path.realpath(src_file))
        if mencoding:
            headers['Content-Encoding'] = mencoding
            logger.debug('Set MIME encoding: {}'.format(mencoding))

        fname = input_name
        if not fname:
            fname = os.path.basename(src_file)

        protocols = self._get_transfer_protocols(archive,
                                                 fname,
                                                 is_get=False,
                                                 headers=headers)
        if len(protocols) == 0:
            raise exceptions.HttpException('No URLs available to put data to')

        # get the list of transfer points
        for protocol in protocols:
            url = protocol.endpoint
            if url is None:
                self.logger.debug('No endpoint for URI, skipping.')
                continue
            self.logger.debug('PUT to URL {}'.format(url))

            try:
                start = time.time()
                with open(src_file, 'rb') as f:
                    self._data_client.put(url, headers=headers, data=f)
                duration = time.time() - start
                stat_info = os.stat(src_file)
                self.logger.info(
                    ('Successfully uploaded archive/file {}/{} in {}s '
                     '(avg. speed: {}MB/s)').format(
                         archive, src_file, round(duration, 2),
                         round(stat_info.st_size / 1024 / 1024 / duration, 2)))
                return
            except (exceptions.HttpException, socket.timeout) as e:
                # try a different URL
                self.logger.info(
                    'WARN: Cannot put data to {}. Exception: {}'.format(
                        url, e))
                self.logger.warn('Try the next URL')
                continue
        raise exceptions.HttpException(
            'Unable to put data from any of the available URLs')