Beispiel #1
0
    def get(self,
            product,
            product_path,
            target_path,
            use_enclosing_directory,
            use_symlinks=None):
        if use_symlinks:
            raise Error("Swift storage backend does not support symlinks")

        archive_path = product.core.archive_path

        keys = self._object_keys(product_path)
        if not keys:
            raise Error("no data for product '%s' (%s)" %
                        (product.core.product_name, product.core.uuid))

        for key in keys:
            rel_path = os.path.relpath(key, archive_path)
            if use_enclosing_directory:
                rel_path = '/'.join(rel_path.split('/')[1:])
            target = os.path.normpath(os.path.join(target_path, rel_path))
            if key.endswith('/'):
                util.make_path(target)
            else:
                util.make_path(os.path.dirname(target))
                binary = self._conn.get_object(self.container, key)[1]
                with open(target, 'wb') as f:
                    f.write(binary)
Beispiel #2
0
def retrieve_function(archive, product, verify_hash_download):
    if getattr(product.core, "archive_path", None) is None:
        raise Error("cannot pull files that do not have archive_path set")

    # determine the backend to use
    backend = None
    url = product.core.remote_url
    for prot in archive.remote_backends():
        _backend = archive.remote_backend(prot)
        if _backend.indentify(url):
            backend = _backend
    if backend is None:
        raise Error("The protocol of '%s' is not supported" % url)

    def retrieve_files(target_dir):
        paths = backend.pull(archive, product, target_dir)

        # check that download matches stored hash
        stored_hash = getattr(product.core, 'hash', None)
        if verify_hash_download and stored_hash is not None:
            hash_type = archive._extract_hash_type(stored_hash)
            if hash_type is None:
                stored_hash = 'sha1:' + stored_hash
                hash_type = 'sha1'
            calc_hash = util.product_hash(paths, hash_type=hash_type)
            if calc_hash != stored_hash:
                raise DownloadError("hash mismatch when pulling product '%s' (%s)" %
                                    (product.core.product_name, product.core.uuid))

        return paths

    return retrieve_files
Beispiel #3
0
    def pull(self, archive, product):
        if getattr(product.core, "archive_path", None) is None:
            raise Error("cannot pull files that do not have archive_path set")

        # Determine the (absolute) path in the archive that will contain the product and create it if required.
        abs_archive_path = os.path.realpath(
            os.path.join(archive._root, product.core.archive_path))
        abs_product_path = os.path.join(abs_archive_path,
                                        product.core.physical_name)

        # Create destination location for product
        try:
            util.make_path(abs_archive_path)
        except EnvironmentError as _error:
            raise Error("cannot create parent destination path '%s' [%s]" %
                        (abs_archive_path, _error))

        plugin = archive.product_type_plugin(product.core.product_type)

        # Create a temporary directory and download the product there, then move the product to its
        # destination within the archive.
        try:
            with util.TemporaryDirectory(prefix=".pull-",
                                         suffix="-%s" % product.core.uuid.hex,
                                         dir=abs_archive_path) as tmp_path:

                # Create enclosing directory if required.
                if plugin.use_enclosing_directory:
                    tmp_path = os.path.join(tmp_path,
                                            product.core.physical_name)
                    util.make_path(tmp_path)

                # Define a temp location and download the file
                tmp_file = os.path.join(tmp_path, product.core.physical_name)
                downloader = util.Downloader(product.core.remote_url,
                                             archive.auth_file())
                downloader.save(tmp_file)

                # TODO: implement extraction of downloaded archives
                # for ftp and file check if url ends with 'core.physical_name + <archive ext>'
                # for http/https check the header for the line:
                #    Content-Disposition: attachment; filename="**********"
                # end then use this ***** filename to match against core.physical_name + <archive ext>

                # Move the transferred product into its destination within the archive.
                if plugin.use_enclosing_directory:
                    os.rename(tmp_path, abs_product_path)
                else:
                    os.rename(tmp_file, abs_product_path)

        except EnvironmentError as _error:
            raise Error(
                "unable to transfer product to destination path '%s' [%s]" %
                (abs_product_path, _error))
Beispiel #4
0
    def get(self,
            product,
            product_path,
            target_path,
            use_enclosing_directory,
            use_symlinks=None):
        if use_symlinks is None:
            use_symlinks = self._use_symlinks

        try:
            if use_symlinks:
                if use_enclosing_directory:
                    for basename in os.listdir(product_path):
                        os.symlink(os.path.join(product_path, basename),
                                   os.path.join(target_path, basename))
                else:
                    os.symlink(
                        product_path,
                        os.path.join(target_path,
                                     os.path.basename(product_path)))
            else:
                if use_enclosing_directory:
                    for basename in os.listdir(product_path):
                        util.copy_path(os.path.join(product_path, basename),
                                       target_path,
                                       resolve_root=True)
                else:
                    util.copy_path(product_path,
                                   target_path,
                                   resolve_root=True)

        except EnvironmentError as _error:
            raise Error("unable to retrieve product '%s' (%s) [%s]" %
                        (product.core.product_name, product.core.uuid, _error))
Beispiel #5
0
 def prepare(self):
     # Create the archive root path.
     try:
         util.make_path(self._root)
     except EnvironmentError as _error:
         raise Error("unable to create archive root path '%s' [%s]" %
                     (self._root, _error))
Beispiel #6
0
 def destroy(self):
     if self.exists():
         try:
             util.remove_path(self._root)
         except EnvironmentError as _error:
             raise Error("unable to remove archive root path '%s' [%s]" %
                         (self._root, _error))
Beispiel #7
0
    def put(self, paths, properties, use_enclosing_directory, use_symlinks=None,
            retrieve_files=None, run_for_product=None):

        if use_symlinks:
            raise Error("S3 storage backend does not support symlinks")

        anything_stored = False

        try:
            archive_path = properties.core.archive_path
            physical_name = properties.core.physical_name

            if not use_enclosing_directory and retrieve_files is None:
                assert(len(paths) == 1 and os.path.basename(paths[0]) == physical_name)

            tmp_root = self.get_tmp_root(properties)
            with util.TemporaryDirectory(dir=tmp_root, prefix=".put-",
                                         suffix="-%s" % properties.core.uuid.hex) as tmp_path:
                if retrieve_files:
                    paths = retrieve_files(tmp_path)

                # Upload file(s)
                for path in paths:
                    key = self._prefix + os.path.join(archive_path, physical_name)

                    # Add enclosing dir
                    if use_enclosing_directory:
                        key = os.path.join(key, os.path.basename(path))

                    if os.path.isdir(path):
                        self._create_dir(key)
                        anything_stored = True

                        for root, subdirs, files in os.walk(path):
                            rel_root = os.path.relpath(root, path)

                            for subdir in subdirs:
                                dirkey = os.path.normpath(os.path.join(key, rel_root, subdir))
                                self._create_dir(dirkey)
                                anything_stored = True

                            for filename in files:
                                filekey = os.path.normpath(os.path.join(key, rel_root, filename))
                                filepath = os.path.join(root, filename)
                                self._upload_file(filekey, filepath)
                                anything_stored = True

                    else:
                        self._upload_file(key, path)
                        anything_stored = True

                if run_for_product is not None:
                    run_for_product(paths)

        except Exception as e:
            raise StorageError(e, anything_stored)
Beispiel #8
0
def pull(archive, product):
    # determine the backend to use
    backend = None
    url = product.core.remote_url
    for prot in archive.remote_backends():
        _backend = archive.remote_backend(prot)
        if _backend.indentify(url):
            backend = _backend
    if backend is None:
        raise Error("The protocol of '%s' is not supported" % url)

    backend.pull(archive, product)
Beispiel #9
0
def pull(archive, product, use_enclosing_directory):
    if getattr(product.core, "archive_path", None) is None:
        raise Error("cannot pull files that do not have archive_path set")

    # determine the backend to use
    backend = None
    url = product.core.remote_url
    for prot in archive.remote_backends():
        _backend = archive.remote_backend(prot)
        if _backend.indentify(url):
            backend = _backend
    if backend is None:
        raise Error("The protocol of '%s' is not supported" % url)

    def retrieve_files(target_dir):
        return backend.pull(archive, product, target_dir)

    archive._storage.put(None,
                         product,
                         use_enclosing_directory,
                         use_symlinks=False,
                         retrieve_files=retrieve_files)
def get_remote_url(filename, ecmwfmars, levtype_options):
    """
        levtype_options should be a dict with for each 'levtype' field a dict containing:
        - string 'param'
            '/' separated list of parameters for the given level type
            https://software.ecmwf.int/wiki/display/UDOC/Identification+keywords#Identificationkeywords-param
        - string 'levelist' (optional)
            '/' separated list of levels for the given level type
            https://software.ecmwf.int/wiki/display/UDOC/Identification+keywords#Identificationkeywords-levelist
    """

    if not levtype_options:
        raise Error("no parameters to construct remote_url")

    remote_url = "ecmwfapi:%s?" % (filename, )
    request = {
        'class': ecmwfmars.marsclass,
        'stream': ecmwfmars.stream,
        'expver': ecmwfmars.expver,
        'type': ecmwfmars.type,
        'date': ecmwfmars.date,
        'time': ecmwfmars.time,
    }
    if 'dataset' in ecmwfmars:
        request['dataset'] = ecmwfmars.dataset
    if 'step' in ecmwfmars:
        request['step'] = str(ecmwfmars.step)
    if 'resol' in ecmwfmars:
        request['resol'] = ecmwfmars.resol
    if 'grid' in ecmwfmars:
        request['grid'] = ecmwfmars.grid
    if 'area' in ecmwfmars:
        request['area'] = ecmwfmars.area

    first = True
    for levtype in levtype_options:
        if first:
            first = False
        else:
            # The '&concatenate&' is a muninn-specific way of combining multiple requests in one
            remote_url += "&concatenate&"
        request['levtype'] = levtype
        request['param'] = levtype_options[levtype]['param']
        if 'levelist' in levtype_options[levtype]:
            request['levelist'] = levtype_options[levtype]['levelist']
        elif 'levelist' in request:
            del request['levelist']
        remote_url += "&".join(
            ["%s=%s" % (key, str(request[key])) for key in request])

    return remote_url
Beispiel #11
0
    def get(self, product, product_path, target_path, use_enclosing_directory, use_symlinks=None):
        if use_symlinks:
            raise Error("S3 storage backend does not support symlinks")

        archive_path = product.core.archive_path
        prefix = self._prefix + product_path

        objs = list(self._resource.Bucket(self.bucket).objects.filter(Prefix=prefix))
        if not objs:
            raise Error("no data for product '%s' (%s)" % (product.core.product_name, product.core.uuid))

        for obj in objs:
            rel_path = os.path.relpath(obj.key, self._prefix + archive_path)
            if use_enclosing_directory:
                rel_path = '/'.join(rel_path.split('/')[1:])
            target = os.path.normpath(os.path.join(target_path, rel_path))

            if obj.key.endswith('/'):
                util.make_path(target)
            else:
                util.make_path(os.path.dirname(target))
                self._resource.Object(self.bucket, obj.key).download_file(target, ExtraArgs=self._download_args,
                                                                          Config=self._transfer_config)
Beispiel #12
0
 def update(self, other):
     '''update a struct, using the same semantics as dict.update()'''
     for key in other:
         other_item = other[key]
         if isinstance(other_item, Struct):
             if key not in self:
                 self[key] = Struct()
             else:
                 if not isinstance(self[key], Struct):
                     raise Error('Incompatible structs: %s vs %s' %
                                 (self, other))
             self[key].update(other_item)
         else:
             self[key] = other_item
Beispiel #13
0
    def current_archive_path(self, paths):
        for path in paths:
            if not util.is_sub_path(
                    os.path.realpath(path), self._root, allow_equal=True):
                raise Error(
                    "cannot ingest a file in-place if it is not inside the muninn archive root"
                )

        abs_archive_path = os.path.dirname(os.path.realpath(paths[0]))

        if len(paths) > 1:
            # check whether all files have the right enclosing directory
            for path in paths:
                enclosing_directory = os.path.basename(
                    os.path.dirname(os.path.realpath(path)))
                if enclosing_directory != properties.core.physical_name:
                    raise Error(
                        "multi-part product has invalid enclosing directory for in-place ingestion"
                    )
            abs_archive_path = os.path.dirname(abs_archive_path)

        # strip archive root
        return os.path.relpath(abs_archive_path,
                               start=os.path.realpath(self._root))
Beispiel #14
0
    def put(self,
            paths,
            properties,
            use_enclosing_directory,
            use_symlinks=None,
            move_files=False,
            retrieve_files=None):
        if use_symlinks:
            raise Error("S3 storage backend does not support symlinks")

        archive_path = properties.core.archive_path
        physical_name = properties.core.physical_name

        tmp_root = self.get_tmp_root(properties)
        with util.TemporaryDirectory(dir=tmp_root,
                                     prefix=".put-",
                                     suffix="-%s" %
                                     properties.core.uuid.hex) as tmp_path:
            if retrieve_files:
                paths = retrieve_files(tmp_path)

            # Upload file(s)
            for path in paths:
                key = self._prefix + os.path.join(archive_path, physical_name)

                # Add enclosing dir
                if use_enclosing_directory:
                    key = os.path.join(key, os.path.basename(path))

                if os.path.isdir(path):
                    for root, subdirs, files in os.walk(path):
                        rel_root = os.path.relpath(root, path)
                        for filename in files:
                            filekey = os.path.normpath(
                                os.path.join(key, rel_root, filename))
                            filepath = os.path.join(root, filename)
                            self._resource.Object(
                                self.bucket, filekey).upload_file(
                                    filepath,
                                    ExtraArgs=self._upload_args,
                                    Config=self._transfer_config)
                else:
                    self._resource.Object(self.bucket, key).upload_file(
                        path,
                        ExtraArgs=self._upload_args,
                        Config=self._transfer_config)
Beispiel #15
0
    def put(self,
            paths,
            properties,
            use_enclosing_directory,
            use_symlinks=None,
            move_files=False,
            retrieve_files=None):
        if use_symlinks:
            raise Error("Swift storage backend does not support symlinks")

        archive_path = properties.core.archive_path
        physical_name = properties.core.physical_name

        tmp_root = self.get_tmp_root(properties)
        with util.TemporaryDirectory(dir=tmp_root,
                                     prefix=".put-",
                                     suffix="-%s" %
                                     properties.core.uuid.hex) as tmp_path:
            if retrieve_files:
                paths = retrieve_files(tmp_path)

            # Upload file(s)
            for path in paths:
                key = os.path.join(archive_path, physical_name)

                # Add enclosing dir
                if use_enclosing_directory:
                    key = os.path.join(key, os.path.basename(path))

                if os.path.isdir(path):
                    for root, subdirs, files in os.walk(path):
                        rel_root = os.path.relpath(root, path)
                        for filename in files:
                            filekey = os.path.normpath(
                                os.path.join(key, rel_root, filename))
                            filepath = os.path.join(root, filename)
                            with open(filepath, 'rb') as f:
                                self._conn.put_object(self.container,
                                                      filekey,
                                                      contents=f.read())
                else:
                    with open(path, 'rb') as f:
                        self._conn.put_object(self.container,
                                              key,
                                              contents=f.read())
Beispiel #16
0
    def move(self, product, archive_path, paths=None):
        # Ignore if product already there
        if product.core.archive_path == archive_path:
            return paths

        product_path = self._prefix + self.product_path(product)
        new_product_path = self._prefix + os.path.join(archive_path, product.core.physical_name)

        objs = list(self._resource.Bucket(self.bucket).objects.filter(Prefix=product_path))
        if not objs:
            raise Error("no data for product '%s' (%s)" % (product.core.product_name, product.core.uuid))

        for obj in objs:
            new_key = os.path.normpath(os.path.join(new_product_path, os.path.relpath(obj.key, product_path)))
            self._resource.Object(self.bucket, new_key).copy(CopySource={'Bucket': self.bucket, 'Key': obj.key},
                                                             ExtraArgs=self._copy_args, Config=self._transfer_config)
            self._resource.Object(self.bucket, obj.key).delete()

        return paths
Beispiel #17
0
    def move(self, product, archive_path, paths=None):
        # Ignore if product already there
        if product.core.archive_path == archive_path:
            return paths

        product_path = self.product_path(product)
        new_product_path = os.path.join(archive_path,
                                        product.core.physical_name)

        keys = self._object_keys(product_path)
        if not keys:
            raise Error("no data for product '%s' (%s)" %
                        (product.core.product_name, product.core.uuid))

        for key in keys:
            new_key = os.path.normpath(
                os.path.join(new_product_path,
                             os.path.relpath(key, product_path)))
            self._conn.copy_object(self.container, key,
                                   os.path.join(self.container, new_key))
            self._conn.delete_object(self.container, key)

        return paths
Beispiel #18
0
    def delete(self, product_path, properties):
        if not os.path.lexists(product_path):
            # If the product does not exist, do not consider this an error.
            return

        try:
            tmp_root = self.get_tmp_root(properties)
            with util.TemporaryDirectory(prefix=".remove-",
                                         suffix="-%s" %
                                         properties.core.uuid.hex,
                                         dir=tmp_root) as tmp_path:

                # Move product into the temporary directory. When the temporary directory will be removed at the end of
                # this scope, the product will be removed along with it.
                assert properties.core.physical_name == os.path.basename(
                    product_path)
                os.rename(
                    product_path,
                    os.path.join(tmp_path, os.path.basename(product_path)))

        except EnvironmentError as _error:
            raise Error(
                "unable to remove product '%s' (%s) [%s]" %
                (properties.core.product_name, properties.core.uuid, _error))
Beispiel #19
0
 def current_archive_path(self, paths, properties):
     raise Error("S3 storage backend does not support ingesting already archived products")
Beispiel #20
0
    def __init__(self, geometries=[]):
        super(LineString, self).__init__(geometries)

        if len(self) == 1:
            raise Error(
                "line string should be empty or should contain >= 2 points")
def extract_grib_metadata(gribfile):
    """
      this will return a tuple containing:
        - ecmwfmars properties struct
        - levtype_options struct (see set_remote_url())
    """
    import coda

    @contextlib.contextmanager
    def coda_open(filename):
        coda_handle = coda.open(filename)
        try:
            yield coda_handle
        finally:
            coda.close(coda_handle)

    ecmwfmars = Struct()
    levtype_options = {}  # TODO: add extraction of levtype_options

    with coda_open(gribfile) as coda_handle:
        cursor = coda.Cursor()
        coda.cursor_set_product(cursor, coda_handle)
        num_messages = coda.cursor_get_num_elements(cursor)
        coda.cursor_goto_first_array_element(cursor)
        for i in range(num_messages):
            index = coda.cursor_get_available_union_field_index(cursor)
            coda.cursor_goto_record_field_by_index(cursor, index)
            step = 0
            if index == 0:
                # grib1
                centuryOfReferenceTimeOfData = coda.fetch(
                    cursor, "centuryOfReferenceTimeOfData")
                yearOfCentury = coda.fetch(cursor, "yearOfCentury")
                month = coda.fetch(cursor, "month")
                day = coda.fetch(cursor, "day")
                date = "%02d%02d-%02d-%02d" % (centuryOfReferenceTimeOfData -
                                               1, yearOfCentury, month, day)
                hour = coda.fetch(cursor, "hour")
                minute = coda.fetch(cursor, "minute")
                time = "%02d:%02d:00" % (hour, minute)
                unitOfTimeRange = coda.fetch(cursor, "unitOfTimeRange")
                if unitOfTimeRange != 0:
                    P1 = coda.fetch(cursor, "P1")
                    if unitOfTimeRange == 1:
                        step = P1
                    elif unitOfTimeRange == 2:
                        step = 24 * P1
                    elif unitOfTimeRange == 10:
                        step = 3 * P1
                    elif unitOfTimeRange == 11:
                        step = 6 * P1
                    elif unitOfTimeRange == 13:
                        step = 12 * P1
                    else:
                        raise Error("unsupported unitOfTimeRange: %d" %
                                    (unitOfTimeRange, ))
                local = coda.fetch(cursor, "local")
                try:
                    local = local[1:9].tobytes()
                except AttributeError:
                    # workaround for older numpy versions
                    local = local[1:9].tostring()
                marsclass, marstype, stream, expver = struct.unpack(
                    '>BBH4s', local)
            else:
                # grib2
                year = coda.fetch(cursor, "year")
                month = coda.fetch(cursor, "month")
                day = coda.fetch(cursor, "day")
                date = "%04d-%02d-%02d" % (year, month, day)
                hour = coda.fetch(cursor, "hour")
                minute = coda.fetch(cursor, "minute")
                second = coda.fetch(cursor, "second")
                time = "%02d:%02d:%02d" % (hour, minute, second)
                significanceOfReferenceTime = coda.fetch(
                    cursor, "significanceOfReferenceTime")
                local = coda.fetch(cursor, "local[0]")
                try:
                    local = local[2:12].tobytes()
                except AttributeError:
                    # workaround for older numpy versions
                    local = local[2:12].tostring()
                marsclass, marstype, stream, expver = struct.unpack(
                    '>HHH4s', local)
                coda.cursor_goto_record_field_by_name(cursor, "data")
                num_data = coda.cursor_get_num_elements(cursor)
                coda.cursor_goto_first_array_element(cursor)
                prev_step = None
                for j in range(num_data):
                    forecastTime = coda.fetch(cursor, "forecastTime")
                    if forecastTime != 0:
                        indicatorOfUnitOfTimeRange = coda.fetch(
                            cursor, "indicatorOfUnitOfTimeRange")
                        if indicatorOfUnitOfTimeRange == 0:
                            # minutes
                            step = 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 1:
                            # hours
                            step = 60 * 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 2:
                            # days
                            step = 24 * 60 * 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 10:
                            # 3 hours
                            step = 3 * 60 * 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 11:
                            # 6 hours
                            step = 6 * 60 * 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 12:
                            # 12 hours
                            step = 12 * 60 * 60 * forecastTime
                        elif indicatorOfUnitOfTimeRange == 13:
                            # seconds
                            step = forecastTime
                        step = int(step / 3600.)  # convert seconds to hours
                        if prev_step is None:
                            prev_step = step
                        elif step != prev_step:
                            raise Error(
                                "not all data has the same 'step' time (%d) (%d)"
                                % (step, prev_step))
                    if j < num_data - 1:
                        coda.cursor_goto_next_array_element(cursor)
                coda.cursor_goto_parent(cursor)
                coda.cursor_goto_parent(cursor)
            if marsclass not in MARSCLASSES:
                raise Error("unsupported MARS class (%d)" % (marsclass, ))
            marsclass = MARSCLASSES[marsclass]
            if marstype not in MARSTYPES:
                raise Error("unsupported MARS type (%d)" % (marstype, ))
            marstype = MARSTYPES[marstype]
            if stream not in MARSSTREAMS:
                raise Error("unsupported MARS stream (%d)" % (stream, ))
            stream = MARSSTREAMS[stream]
            if 'date' in ecmwfmars:
                if date != ecmwfmars.date:
                    raise Error("not all data is for the same date (%s) (%s)" %
                                (date, ecmwfmars.date))
                if time != ecmwfmars.time:
                    raise Error("not all data is for the same time (%s) (%s)" %
                                (time, ecmwfmars.time))
                if step != 0:
                    if 'step' in ecmwfmars:
                        if step != ecmwfmars.step:
                            raise Error(
                                "not all data has the same 'step' time (%d) (%d)"
                                % (step, ecmwfmars.step))
                    else:
                        raise Error("not all data has the same 'step' time")
                else:
                    if 'step' in ecmwfmars and ecmwfmars.step != 0:
                        raise Error("not all data has the same 'step' time")
                if marsclass != ecmwfmars.marsclass:
                    raise Error(
                        "not all data has the same MARS class (%s) (%s)" %
                        (marsclass, ecmwfmars.marsclass))
                if marstype != ecmwfmars.type:
                    raise Error(
                        "not all data has the same MARS type (%s) (%s)" %
                        (marstype, ecmwfmars.type))
                if stream != ecmwfmars.stream:
                    raise Error(
                        "not all data has the same MARS stream (%s) (%s)" %
                        (stream, ecmwfmars.stream))
                if expver != ecmwfmars.expver:
                    raise Error(
                        "not all data has the same MARS experiment version (%s) (%s)"
                        % (expver, ecmwfmars.expver))
            else:
                ecmwfmars.date = date
                ecmwfmars.time = time
                if step != 0:
                    ecmwfmars.step = step
                ecmwfmars.marsclass = marsclass
                ecmwfmars.type = marstype
                ecmwfmars.stream = stream
                ecmwfmars.expver = expver
                if (marsclass, stream, expver, marstype) in PUBLIC_DATASETS:
                    ecmwfmars.dataset = PUBLIC_DATASETS[(marsclass, stream,
                                                         expver, marstype)]
            coda.cursor_goto_parent(cursor)
            if i < num_messages - 1:
                coda.cursor_goto_next_array_element(cursor)

    return ecmwfmars, levtype_options
Beispiel #22
0
    def put(self,
            paths,
            properties,
            use_enclosing_directory,
            use_symlinks=None,
            retrieve_files=None):
        if use_symlinks is None:
            use_symlinks = self._use_symlinks

        physical_name = properties.core.physical_name
        archive_path = properties.core.archive_path
        uuid = properties.core.uuid

        abs_archive_path = os.path.realpath(
            os.path.join(self._root, archive_path))
        abs_product_path = os.path.join(abs_archive_path, physical_name)

        # TODO separate this out like 'current_archive_path'
        if paths is not None and util.is_sub_path(os.path.realpath(paths[0]),
                                                  abs_product_path,
                                                  allow_equal=True):
            # Product should already be in the target location
            for path in paths:
                if not os.path.exists(path):
                    raise Error("product source path does not exist '%s'" %
                                (path, ))
                if not util.is_sub_path(os.path.realpath(path),
                                        abs_product_path,
                                        allow_equal=True):
                    raise Error(
                        "cannot ingest product where only part of the files are already at the "
                        "destination location")
        else:
            # Create destination location for product
            try:
                util.make_path(abs_archive_path)
            except EnvironmentError as _error:
                raise Error("cannot create parent destination path '%s' [%s]" %
                            (abs_archive_path, _error))

            # Create a temporary directory and transfer the product there, then move the product to its
            # destination within the archive.
            try:
                tmp_root = self.get_tmp_root(properties)
                with util.TemporaryDirectory(prefix=".put-",
                                             suffix="-%s" % uuid.hex,
                                             dir=tmp_root) as tmp_path:

                    # Create enclosing directory if required.
                    if use_enclosing_directory:
                        tmp_path = os.path.join(tmp_path, physical_name)
                        util.make_path(tmp_path)

                    # Transfer the product (parts).
                    if use_symlinks:
                        if use_enclosing_directory:
                            abs_path = abs_product_path
                        else:
                            abs_path = abs_archive_path

                        # Create symbolic link(s) for the product (parts).
                        for path in paths:
                            if util.is_sub_path(path, self._root):
                                # Create a relative symbolic link when the target is part of the archive
                                # (i.e. when creating an intra-archive symbolic link). This ensures the
                                # archive can be relocated without breaking intra-archive symbolic links.
                                os.symlink(
                                    os.path.relpath(path, abs_path),
                                    os.path.join(tmp_path,
                                                 os.path.basename(path)))
                            else:
                                os.symlink(
                                    path,
                                    os.path.join(tmp_path,
                                                 os.path.basename(path)))
                    else:
                        # Copy/retrieve product (parts).
                        if retrieve_files:
                            paths = retrieve_files(tmp_path)
                        else:
                            for path in paths:
                                util.copy_path(path,
                                               tmp_path,
                                               resolve_root=True)

                    # Move the transferred product into its destination within the archive.
                    if use_enclosing_directory:
                        os.rename(tmp_path, abs_product_path)
                    else:
                        assert (len(paths) == 1 and os.path.basename(paths[0])
                                == physical_name)
                        tmp_product_path = os.path.join(
                            tmp_path, physical_name)
                        os.rename(tmp_product_path, abs_product_path)

            except EnvironmentError as _error:
                raise Error(
                    "unable to transfer product to destination path '%s' [%s]"
                    % (abs_product_path, _error))
    def pull(self, archive, product):
        from ecmwfapi import ECMWFDataServer, ECMWFService
        dataserver = ECMWFDataServer(log=logging.info)
        marsservice = ECMWFService("mars", log=logging.info)

        if getattr(product.core, "archive_path", None) is None:
            raise Error("cannot pull files that do not have archive_path set")

        # Determine the (absolute) path in the archive that will contain the product and create it if required.
        abs_archive_path = os.path.realpath(
            os.path.join(archive._root, product.core.archive_path))
        abs_product_path = os.path.join(abs_archive_path,
                                        product.core.physical_name)

        # Create destination location for product
        try:
            util.make_path(abs_archive_path)
        except EnvironmentError as _error:
            raise Error("cannot create parent destination path '%s' [%s]" %
                        (abs_archive_path, _error))

        requests = []
        for order in product.core.remote_url.split('?')[1].split(
                '&concatenate&'):
            request = {}
            for param in order.split('&'):
                key, value = param.split('=')
                request[key] = value
            requests.append(request)

        plugin = archive.product_type_plugin(product.core.product_type)

        # Create a temporary directory and download the product there, then move the product to its
        # destination within the archive.
        try:
            with util.TemporaryDirectory(prefix=".pull-",
                                         suffix="-%s" % product.core.uuid.hex,
                                         dir=abs_archive_path) as tmp_path:

                # Create enclosing directory if required.
                if plugin.use_enclosing_directory:
                    tmp_path = os.path.join(tmp_path,
                                            product.core.physical_name)
                    util.make_path(tmp_path)

                # Download product.
                tmp_file_combined = os.path.join(tmp_path,
                                                 product.core.physical_name)
                tmp_file_download = os.path.join(tmp_path, "request.grib")
                combined_file = open(tmp_file_combined, "w")
                for request in requests:
                    if 'dataset' in request:
                        request['target'] = tmp_file_download
                        dataserver.retrieve(request)
                    else:
                        marsservice.execute(request, tmp_file_download)
                    result_file = open(tmp_file_download, "r")
                    combined_file.write(result_file.read())
                    result_file.close()
                    os.remove(tmp_file_download)
                combined_file.close()

                # Move the retrieved product into its destination within the archive.
                if plugin.use_enclosing_directory:
                    os.rename(tmp_path, abs_product_path)
                else:
                    os.rename(tmp_file_combined, abs_product_path)

        except EnvironmentError as _error:
            raise Error(
                "unable to transfer product to destination path '%s' [%s]" %
                (abs_product_path, _error))