def get_classic_link(vpc, **conn):
    """Gets the Classic Link details about a VPC"""
    result = {}

    try:
        cl_result = describe_vpc_classic_link(VpcIds=[vpc["id"]], **conn)[0]
        result["Enabled"] = cl_result["ClassicLinkEnabled"]

        # Check for DNS as well:
        dns_result = describe_vpc_classic_link_dns_support(VpcIds=[vpc["id"]], **conn)[0]
        result["DnsEnabled"] = dns_result["ClassicLinkDnsSupported"]
    except ClientError as e:
        # This is not supported for all regions.
        if 'UnsupportedOperation' not in str(e):
            raise e

    return result

def get_classic_link ( vpc , ** conn ) : result = { } try : cl_result = describe_vpc_classic_link ( VpcIds = [ vpc [ "id" ] ] , ** conn ) [ 0 ] result [ "Enabled" ] = cl_result [ "ClassicLinkEnabled" ] dns_result = describe_vpc_classic_link_dns_support ( VpcIds = [ vpc [ "id" ] ] , ** conn ) [ 0 ] result [ "DnsEnabled" ] = dns_result [ "ClassicLinkDnsSupported" ] except ClientError as e : if 'UnsupportedOperation' not in str ( e ) : raise e return result


def get_subnets(vpc, **conn):
    """Gets the VPC Subnets"""
    subnets = describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc["id"]]}], **conn)

    s_ids = []
    for s in subnets:
        s_ids.append(s["SubnetId"])

    return s_ids

def get_subnets ( vpc , ** conn ) : subnets = describe_subnets ( Filters = [ { "Name" : "vpc-id" , "Values" : [ vpc [ "id" ] ] } ] , ** conn ) s_ids = [ ] for s in subnets : s_ids . append ( s [ "SubnetId" ] ) return s_ids


def get_route_tables(vpc, **conn):
    """Gets the VPC Route Tables"""
    route_tables = describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [vpc["id"]]}], **conn)

    rt_ids = []
    for r in route_tables:
        rt_ids.append(r["RouteTableId"])

    return rt_ids

def get_route_tables ( vpc , ** conn ) : route_tables = describe_route_tables ( Filters = [ { "Name" : "vpc-id" , "Values" : [ vpc [ "id" ] ] } ] , ** conn ) rt_ids = [ ] for r in route_tables : rt_ids . append ( r [ "RouteTableId" ] ) return rt_ids


def get_network_acls(vpc, **conn):
    """Gets the VPC Network ACLs"""
    route_tables = describe_network_acls(Filters=[{"Name": "vpc-id", "Values": [vpc["id"]]}], **conn)

    nacl_ids = []
    for r in route_tables:
        nacl_ids.append(r["NetworkAclId"])

    return nacl_ids

def get_network_acls ( vpc , ** conn ) : route_tables = describe_network_acls ( Filters = [ { "Name" : "vpc-id" , "Values" : [ vpc [ "id" ] ] } ] , ** conn ) nacl_ids = [ ] for r in route_tables : nacl_ids . append ( r [ "NetworkAclId" ] ) return nacl_ids


def get_client(service, service_type='client', **conn_args):
    """
    User function to get the correct client.

    Based on the GOOGLE_CLIENT_MAP dictionary, the return will be a cloud or general
    client that can interact with the desired service.

    :param service: GCP service to connect to. E.g. 'gce', 'iam'
    :type service: ``str``

    :param conn_args: Dictionary of connection arguments.  'project' is required.
                      'user_agent' can be specified and will be set in the client
                       returned.
    :type conn_args: ``dict``

    :return: client_details, client
    :rtype: ``tuple`` of ``dict``, ``object``
    """
    client_details = choose_client(service)
    user_agent = get_user_agent(**conn_args)
    if client_details:
        if client_details['client_type'] == 'cloud':
            client = get_gcp_client(
                mod_name=client_details['module_name'],
                pkg_name=conn_args.get('pkg_name', 'google.cloud'),
                key_file=conn_args.get('key_file', None),
                project=conn_args['project'], user_agent=user_agent)
        else:
            client = get_google_client(
                mod_name=client_details['module_name'],
                key_file=conn_args.get('key_file', None),
                user_agent=user_agent, api_version=conn_args.get('api_version', 'v1'))
    else:
        # There is no client known for this service. We can try the standard API.
        try:
            client = get_google_client(
                mod_name=service, key_file=conn_args.get('key_file', None),
                user_agent=user_agent, api_version=conn_args.get('api_version', 'v1'))
        except Exception as e:
            raise e

    return client_details, client

def get_client ( service , service_type = 'client' , ** conn_args ) : client_details = choose_client ( service ) user_agent = get_user_agent ( ** conn_args ) if client_details : if client_details [ 'client_type' ] == 'cloud' : client = get_gcp_client ( mod_name = client_details [ 'module_name' ] , pkg_name = conn_args . get ( 'pkg_name' , 'google.cloud' ) , key_file = conn_args . get ( 'key_file' , None ) , project = conn_args [ 'project' ] , user_agent = user_agent ) else : client = get_google_client ( mod_name = client_details [ 'module_name' ] , key_file = conn_args . get ( 'key_file' , None ) , user_agent = user_agent , api_version = conn_args . get ( 'api_version' , 'v1' ) ) else : try : client = get_google_client ( mod_name = service , key_file = conn_args . get ( 'key_file' , None ) , user_agent = user_agent , api_version = conn_args . get ( 'api_version' , 'v1' ) ) except Exception as e : raise e return client_details , client

User function to get the correct client .


def get_gcp_client(**kwargs):
    """Public GCP client builder."""
    return _gcp_client(project=kwargs['project'], mod_name=kwargs['mod_name'],
                       pkg_name=kwargs.get('pkg_name', 'google.cloud'),
                       key_file=kwargs.get('key_file', None),
                       http_auth=kwargs.get('http', None),
                       user_agent=kwargs.get('user_agent', None))

def get_gcp_client ( ** kwargs ) : return _gcp_client ( project = kwargs [ 'project' ] , mod_name = kwargs [ 'mod_name' ] , pkg_name = kwargs . get ( 'pkg_name' , 'google.cloud' ) , key_file = kwargs . get ( 'key_file' , None ) , http_auth = kwargs . get ( 'http' , None ) , user_agent = kwargs . get ( 'user_agent' , None ) )


def _gcp_client(project, mod_name, pkg_name, key_file=None, http_auth=None,
                user_agent=None):
    """
    Private GCP client builder.

    :param project: Google Cloud project string.
    :type project: ``str``

    :param mod_name: Module name to load.  Should be found in sys.path.
    :type mod_name: ``str``

    :param pkg_name: package name that mod_name is part of.  Default is 'google.cloud' .
    :type pkg_name: ``str``

    :param key_file: Default is None.
    :type key_file: ``str`` or None

    :param http_auth: httplib2 authorized client. Default is None.
    :type http_auth: :class: `HTTPLib2`

    :param user_agent: User Agent string to use in requests. Default is None.
    :type http_auth: ``str`` or None

    :return: GCP client
    :rtype: ``object``
    """
    client = None
    if http_auth is None:
        http_auth = _googleauth(key_file=key_file, user_agent=user_agent)
    try:
        # Using a relative path, so we prefix with a dot (.)
        google_module = importlib.import_module('.' + mod_name,
                                                package=pkg_name)
        client = google_module.Client(use_GAX=USE_GAX, project=project,
                                      http=http_auth)
    except ImportError as ie:
        import_err = 'Unable to import %s.%s' % (pkg_name, mod_name)
        raise ImportError(import_err)
    except TypeError:
        # Not all clients use gRPC
        client = google_module.Client(project=project, http=http_auth)
    if user_agent and hasattr(client, 'user_agent'):
        client.user_agent = user_agent
    return client

def
_gcp_client
(
project
,
mod_name
,
pkg_name
,
key_file
=
,
http_auth
=
,
user_agent
=
)
:
client
=
if
http_auth
is
:
http_auth
=
_googleauth
(
key_file
=
key_file
,
user_agent
=
user_agent
)
try
:
google_module
=
importlib
.
import_module
(
'.'
+
mod_name
,
package
=
pkg_name
)
client
=
google_module
.
Client
(
use_GAX
=
USE_GAX
,
project
=
project
,
http
=
http_auth
)
except
ImportError
as
ie
:
import_err
=
'Unable to import %s.%s'
%
(
pkg_name
,
mod_name
)
raise
ImportError
(
import_err
)
except
TypeError
:
client
=
google_module
.
Client
(
project
=
project
,
http
=
http_auth
)
if
user_agent
and
hasattr
(
client
,
'user_agent'
)
:
client
.
user_agent
=
user_agent
return
client


def _googleauth(key_file=None, scopes=[], user_agent=None):
    """
    Google http_auth helper.

    If key_file is not specified, default credentials will be used.

    If scopes is specified (and key_file), will be used instead of DEFAULT_SCOPES

    :param key_file: path to key file to use. Default is None
    :type key_file: ``str``

    :param scopes: scopes to set.  Default is DEFAUL_SCOPES
    :type scopes: ``list``

    :param user_agent: User Agent string to use in requests. Default is None.
    :type http_auth: ``str`` or None

    :return: HTTPLib2 authorized client.
    :rtype: :class: `HTTPLib2`
    """
    if key_file:
        if not scopes:
            scopes = DEFAULT_SCOPES
        creds = ServiceAccountCredentials.from_json_keyfile_name(key_file,
                                                                 scopes=scopes)
    else:
        creds = GoogleCredentials.get_application_default()
    http = Http()
    if user_agent:
        http = set_user_agent(http, user_agent)
    http_auth = creds.authorize(http)
    return http_auth

def _googleauth ( key_file = None , scopes = [ ] , user_agent = None ) : if key_file : if not scopes : scopes = DEFAULT_SCOPES creds = ServiceAccountCredentials . from_json_keyfile_name ( key_file , scopes = scopes ) else : creds = GoogleCredentials . get_application_default ( ) http = Http ( ) if user_agent : http = set_user_agent ( http , user_agent ) http_auth = creds . authorize ( http ) return http_auth


def _build_google_client(service, api_version, http_auth):
    """
    Google build client helper.

    :param service: service to build client for
    :type service: ``str``

    :param api_version: API version to use.
    :type api_version: ``str``

    :param http_auth: Initialized HTTP client to use.
    :type http_auth: ``object``

    :return: google-python-api client initialized to use 'service'
    :rtype: ``object``
    """
    client = build(service, api_version, http=http_auth)
    return client

def _build_google_client ( service , api_version , http_auth ) : client = build ( service , api_version , http = http_auth ) return client


def iter_project(projects, key_file=None):
    """
    Call decorated function for each item in project list.

    Note: the function 'decorated' is expected to return a value plus a dictionary of exceptions.

    If item in list is a dictionary, we look for a 'project' and 'key_file' entry, respectively.
    If item in list is of type string_types, we assume it is the project string. Default credentials
    will be used by the underlying client library.

    :param projects: list of project strings or list of dictionaries
                     Example: {'project':..., 'keyfile':...}. Required.
    :type projects: ``list`` of ``str`` or ``list`` of ``dict``

    :param key_file: path on disk to keyfile, for use with all projects
    :type key_file: ``str``

    :returns: tuple containing a list of function output and an exceptions map
    :rtype: ``tuple of ``list``, ``dict``
    """

    def decorator(func):
        @wraps(func)
        def decorated_function(*args, **kwargs):
            item_list = []
            exception_map = {}
            for project in projects:
                if isinstance(project, string_types):
                    kwargs['project'] = project
                    if key_file:
                        kwargs['key_file'] = key_file
                elif isinstance(project, dict):
                    kwargs['project'] = project['project']
                    kwargs['key_file'] = project['key_file']
                itm, exc = func(*args, **kwargs)
                item_list.extend(itm)
                exception_map.update(exc)
            return (item_list, exception_map)

        return decorated_function

    return decorator

def iter_project ( projects , key_file = None ) : def decorator ( func ) : @ wraps ( func ) def decorated_function ( * args , ** kwargs ) : item_list = [ ] exception_map = { } for project in projects : if isinstance ( project , string_types ) : kwargs [ 'project' ] = project if key_file : kwargs [ 'key_file' ] = key_file elif isinstance ( project , dict ) : kwargs [ 'project' ] = project [ 'project' ] kwargs [ 'key_file' ] = project [ 'key_file' ] itm , exc = func ( * args , ** kwargs ) item_list . extend ( itm ) exception_map . update ( exc ) return ( item_list , exception_map ) return decorated_function return decorator

Call decorated function for each item in project list .


def get_creds_from_kwargs(kwargs):
    """Helper to get creds out of kwargs."""
    creds = {
        'key_file': kwargs.pop('key_file', None),
        'http_auth': kwargs.pop('http_auth', None),
        'project': kwargs.get('project', None),
        'user_agent': kwargs.pop('user_agent', None),
        'api_version': kwargs.pop('api_version', 'v1')
    }
    return (creds, kwargs)

def get_creds_from_kwargs ( kwargs ) : creds = { 'key_file' : kwargs . pop ( 'key_file' , None ) , 'http_auth' : kwargs . pop ( 'http_auth' , None ) , 'project' : kwargs . get ( 'project' , None ) , 'user_agent' : kwargs . pop ( 'user_agent' , None ) , 'api_version' : kwargs . pop ( 'api_version' , 'v1' ) } return ( creds , kwargs )


def rewrite_kwargs(conn_type, kwargs, module_name=None):
    """
    Manipulate connection keywords.

    Modifieds keywords based on connection type.

    There is an assumption here that the client has
    already been created and that these keywords are being
    passed into methods for interacting with various services.

    Current modifications:
    - if conn_type is not cloud and module is 'compute',
      then rewrite project as name.
    - if conn_type is cloud and module is 'storage',
      then remove 'project' from dict.

    :param conn_type: E.g. 'cloud' or 'general'
    :type conn_type: ``str``

    :param kwargs: Dictionary of keywords sent in by user.
    :type kwargs: ``dict``

    :param module_name: Name of specific module that will be loaded.
                        Default is None.
    :type conn_type: ``str`` or None

    :returns kwargs with client and module specific changes
    :rtype: ``dict``
    """
    if conn_type != 'cloud' and module_name != 'compute':
        if 'project' in kwargs:
            kwargs['name'] = 'projects/%s' % kwargs.pop('project')
    if conn_type == 'cloud' and module_name == 'storage':
        if 'project' in kwargs:
            del kwargs['project']
    return kwargs

def rewrite_kwargs ( conn_type , kwargs , module_name = None ) : if conn_type != 'cloud' and module_name != 'compute' : if 'project' in kwargs : kwargs [ 'name' ] = 'projects/%s' % kwargs . pop ( 'project' ) if conn_type == 'cloud' and module_name == 'storage' : if 'project' in kwargs : del kwargs [ 'project' ] return kwargs

Manipulate connection keywords . Modifieds keywords based on connection type .


def gce_list_aggregated(service=None, key_name='name', **kwargs):
    """General aggregated list function for the GCE service."""
    resp_list = []
    req = service.aggregatedList(**kwargs)

    while req is not None:
        resp = req.execute()
        for location, item in resp['items'].items():
            if key_name in item:
                resp_list.extend(item[key_name])

        req = service.aggregatedList_next(previous_request=req,
                                          previous_response=resp)
    return resp_list

def gce_list_aggregated ( service = None , key_name = 'name' , ** kwargs ) : resp_list = [ ] req = service . aggregatedList ( ** kwargs ) while req is not None : resp = req . execute ( ) for location , item in resp [ 'items' ] . items ( ) : if key_name in item : resp_list . extend ( item [ key_name ] ) req = service . aggregatedList_next ( previous_request = req , previous_response = resp ) return resp_list

General aggregated list function for the GCE service .


def gce_list(service=None, **kwargs):
    """General list function for the GCE service."""
    resp_list = []
    req = service.list(**kwargs)

    while req is not None:
        resp = req.execute()
        for item in resp.get('items', []):
            resp_list.append(item)
        req = service.list_next(previous_request=req, previous_response=resp)
    return resp_list

def gce_list ( service = None , ** kwargs ) : resp_list = [ ] req = service . list ( ** kwargs ) while req is not None : resp = req . execute ( ) for item in resp . get ( 'items' , [ ] ) : resp_list . append ( item ) req = service . list_next ( previous_request = req , previous_response = resp ) return resp_list

General list function for the GCE service .


def service_list(service=None, key_name=None, **kwargs):
    """General list function for Google APIs."""
    resp_list = []
    req = service.list(**kwargs)

    while req is not None:
        resp = req.execute()
        if key_name and key_name in resp:
            resp_list.extend(resp[key_name])
        else:
            resp_list.append(resp)
        # Not all list calls have a list_next
        if hasattr(service, 'list_next'):
            req = service.list_next(previous_request=req,
                                    previous_response=resp)
        else:
            req = None
    return resp_list

def service_list ( service = None , key_name = None , ** kwargs ) : resp_list = [ ] req = service . list ( ** kwargs ) while req is not None : resp = req . execute ( ) if key_name and key_name in resp : resp_list . extend ( resp [ key_name ] ) else : resp_list . append ( resp ) if hasattr ( service , 'list_next' ) : req = service . list_next ( previous_request = req , previous_response = resp ) else : req = None return resp_list


def get_cache_access_details(key=None):
    """Retrieve detailed cache information."""
    from cloudaux.gcp.decorators import _GCP_CACHE
    return _GCP_CACHE.get_access_details(key=key)

def get_cache_access_details ( key = None ) : from cloudaux . gcp . decorators import _GCP_CACHE return _GCP_CACHE . get_access_details ( key = key )


def get_user_agent_default(pkg_name='cloudaux'):
    """
    Get default User Agent String.

    Try to import pkg_name to get an accurate version number.

    return: string
    """
    version = '0.0.1'
    try:
        import pkg_resources
        version = pkg_resources.get_distribution(pkg_name).version
    except pkg_resources.DistributionNotFound:
        pass
    except ImportError:
        pass

    return 'cloudaux/%s' % (version)

def get_user_agent_default ( pkg_name = 'cloudaux' ) : version = '0.0.1' try : import pkg_resources version = pkg_resources . get_distribution ( pkg_name ) . version except pkg_resources . DistributionNotFound : pass except ImportError : pass return 'cloudaux/%s' % ( version )


def list_rules(client=None, **kwargs):
    """
    NamePrefix='string'
    """
    result = client.list_rules(**kwargs)
    if not result.get("Rules"):
        result.update({"Rules": []})

    return result

def list_rules ( client = None , ** kwargs ) : result = client . list_rules ( ** kwargs ) if not result . get ( "Rules" ) : result . update ( { "Rules" : [ ] } ) return result


def list_targets_by_rule(client=None, **kwargs):
    """
    Rule='string'
    """
    result = client.list_targets_by_rule(**kwargs)
    if not result.get("Targets"):
        result.update({"Targets": []})

    return result

def list_targets_by_rule ( client = None , ** kwargs ) : result = client . list_targets_by_rule ( ** kwargs ) if not result . get ( "Targets" ) : result . update ( { "Targets" : [ ] } ) return result


def list_buckets(client=None, **kwargs):
    """
    List buckets for a project.

    :param client: client object to use.
    :type client: Google Cloud Storage client

    :returns: list of dictionary reprsentation of Bucket
    :rtype: ``list`` of ``dict``
    """
    buckets = client.list_buckets(**kwargs)
    return [b.__dict__ for b in buckets]

def list_buckets ( client = None , ** kwargs ) : buckets = client . list_buckets ( ** kwargs ) return [ b . __dict__ for b in buckets ]


def list_objects_in_bucket(**kwargs):
    """
    List objects in bucket.

    :param Bucket: name of bucket
    :type Bucket: ``str``

    :returns list of objects in bucket
    :rtype: ``list``
    """
    bucket = get_bucket(**kwargs)
    if bucket:
        return [o for o in bucket.list_blobs()]
    else:
        return None

def list_objects_in_bucket ( ** kwargs ) : bucket = get_bucket ( ** kwargs ) if bucket : return [ o for o in bucket . list_blobs ( ) ] else : return None


def modify(item, output='camelized'):
    """
    Calls _modify and either passes the inflection.camelize method or the inflection.underscore method.

    :param item: dictionary representing item to be modified
    :param output: string 'camelized' or 'underscored'
    :return:
    """
    if output == 'camelized':
        return _modify(item, camelize)
    elif output == 'underscored':
        return _modify(item, underscore)

def modify ( item , output = 'camelized' ) : if output == 'camelized' : return _modify ( item , camelize ) elif output == 'underscored' : return _modify ( item , underscore )

Calls _modify and either passes the inflection . camelize method or the inflection . underscore method .


def get_role_managed_policy_documents(role, client=None, **kwargs):
    """Retrieve the currently active policy version document for every managed policy that is attached to the role."""
    policies = get_role_managed_policies(role, force_client=client)

    policy_names = (policy['name'] for policy in policies)
    delayed_gmpd_calls = (delayed(get_managed_policy_document)(policy['arn'], force_client=client) for policy
                          in policies)
    policy_documents = Parallel(n_jobs=20, backend="threading")(delayed_gmpd_calls)

    return dict(zip(policy_names, policy_documents))

def get_role_managed_policy_documents ( role , client = None , ** kwargs ) : policies = get_role_managed_policies ( role , force_client = client ) policy_names = ( policy [ 'name' ] for policy in policies ) delayed_gmpd_calls = ( delayed ( get_managed_policy_document ) ( policy [ 'arn' ] , force_client = client ) for policy in policies ) policy_documents = Parallel ( n_jobs = 20 , backend = "threading" ) ( delayed_gmpd_calls ) return dict ( zip ( policy_names , policy_documents ) )

Retrieve the currently active policy version document for every managed policy that is attached to the role .


def get_group(group_name, users=True, client=None, **kwargs):
    """Get's the IAM Group details.

    :param group_name:
    :param users: Optional -- will return the IAM users that the group is attached to if desired (paginated).
    :param client:
    :param kwargs:
    :return:
    """
    # First, make the initial call to get the details for the group:
    result = client.get_group(GroupName=group_name, **kwargs)

    # If we care about the user details, then fetch them:
    if users:
        if result.get('IsTruncated'):
            kwargs_to_send = {'GroupName': group_name}
            kwargs_to_send.update(kwargs)

            user_list = result['Users']
            kwargs_to_send['Marker'] = result['Marker']

            result['Users'] = user_list + _get_users_for_group(client, **kwargs_to_send)

    else:
        result.pop('Users', None)
        result.pop('IsTruncated', None)
        result.pop('Marker', None)

    return result

def get_group ( group_name , users = True , client = None , ** kwargs ) : result = client . get_group ( GroupName = group_name , ** kwargs ) if users : if result . get ( 'IsTruncated' ) : kwargs_to_send = { 'GroupName' : group_name } kwargs_to_send . update ( kwargs ) user_list = result [ 'Users' ] kwargs_to_send [ 'Marker' ] = result [ 'Marker' ] result [ 'Users' ] = user_list + _get_users_for_group ( client , ** kwargs_to_send ) else : result . pop ( 'Users' , None ) result . pop ( 'IsTruncated' , None ) result . pop ( 'Marker' , None ) return result


def get_group_policy_document(group_name, policy_name, client=None, **kwargs):
    """Fetches the specific IAM group inline-policy document."""
    return client.get_group_policy(GroupName=group_name, PolicyName=policy_name, **kwargs)['PolicyDocument']

def get_group_policy_document ( group_name , policy_name , client = None , ** kwargs ) : return client . get_group_policy ( GroupName = group_name , PolicyName = policy_name , ** kwargs ) [ 'PolicyDocument' ]

Fetches the specific IAM group inline - policy document .


def _get_base(server_certificate, **conn):
    """Fetch the base IAM Server Certificate."""
    server_certificate['_version'] = 1

    # Get the initial cert details:
    cert_details = get_server_certificate_api(server_certificate['ServerCertificateName'], **conn)

    if cert_details:
        server_certificate.update(cert_details['ServerCertificateMetadata'])
        server_certificate['CertificateBody'] = cert_details['CertificateBody']
        server_certificate['CertificateChain'] = cert_details.get('CertificateChain', None)

        # Cast dates from a datetime to something JSON serializable.
        server_certificate['UploadDate'] = get_iso_string(server_certificate['UploadDate'])
        server_certificate['Expiration'] = get_iso_string(server_certificate['Expiration'])

    return server_certificate

def _get_base ( server_certificate , ** conn ) : server_certificate [ '_version' ] = 1 cert_details = get_server_certificate_api ( server_certificate [ 'ServerCertificateName' ] , ** conn ) if cert_details : server_certificate . update ( cert_details [ 'ServerCertificateMetadata' ] ) server_certificate [ 'CertificateBody' ] = cert_details [ 'CertificateBody' ] server_certificate [ 'CertificateChain' ] = cert_details . get ( 'CertificateChain' , None ) server_certificate [ 'UploadDate' ] = get_iso_string ( server_certificate [ 'UploadDate' ] ) server_certificate [ 'Expiration' ] = get_iso_string ( server_certificate [ 'Expiration' ] ) return server_certificate


def boto3_cached_conn(service, service_type='client', future_expiration_minutes=15, account_number=None,
                      assume_role=None, session_name='cloudaux', region='us-east-1', return_credentials=False,
                      external_id=None, arn_partition='aws'):
    """
    Used to obtain a boto3 client or resource connection.
    For cross account, provide both account_number and assume_role.

    :usage:

    # Same Account:
    client = boto3_cached_conn('iam')
    resource = boto3_cached_conn('iam', service_type='resource')

    # Cross Account Client:
    client = boto3_cached_conn('iam', account_number='000000000000', assume_role='role_name')

    # Cross Account Resource:
    resource = boto3_cached_conn('iam', service_type='resource', account_number='000000000000', assume_role='role_name')

    :param service: AWS service (i.e. 'iam', 'ec2', 'kms')
    :param service_type: 'client' or 'resource'
    :param future_expiration_minutes: Connections will expire from the cache
        when their expiration is within this many minutes of the present time. [Default 15]
    :param account_number: Required if assume_role is provided.
    :param assume_role:  Name of the role to assume into for account described by account_number.
    :param session_name: Session name to attach to requests. [Default 'cloudaux']
    :param region: Region name for connection. [Default us-east-1]
    :param return_credentials: Indicates if the STS credentials should be returned with the client [Default False]
    :param external_id: Optional external id to pass to sts:AssumeRole.
        See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
    :param arn_partition: Optional parameter to specify other aws partitions such as aws-us-gov for aws govcloud
    :return: boto3 client or resource connection
    """
    key = (
        account_number,
        assume_role,
        session_name,
        external_id,
        region,
        service_type,
        service,
        arn_partition
    )

    if key in CACHE:
        retval = _get_cached_creds(key, service, service_type, region, future_expiration_minutes, return_credentials)
        if retval:
            return retval

    role = None
    if assume_role:
        sts = boto3.session.Session().client('sts')

        # prevent malformed ARN
        if not all([account_number, assume_role]):
            raise ValueError("Account number and role to assume are both required")

        arn = 'arn:{partition}:iam::{0}:role/{1}'.format(
            account_number,
            assume_role,
            partition=arn_partition
        )

        assume_role_kwargs = {
            'RoleArn': arn,
            'RoleSessionName': session_name
        }

        if external_id:
            assume_role_kwargs['ExternalId'] = external_id

        role = sts.assume_role(**assume_role_kwargs)

    if service_type == 'client':
        conn = _client(service, region, role)
    elif service_type == 'resource':
        conn = _resource(service, region, role)

    if role:
        CACHE[key] = role

    if return_credentials:
        return conn, role['Credentials']

    return conn

Used to obtain a boto3 client or resource connection . For cross account provide both account_number and assume_role .


def get_rules(security_group, **kwargs):
    """ format the rule fields to match AWS to support auditor reuse,
        will need to remap back if we want to orchestrate from our stored items """
    rules = security_group.pop('security_group_rules',[])
    for rule in rules:
        rule['ip_protocol'] = rule.pop('protocol')
        rule['from_port'] = rule.pop('port_range_max')
        rule['to_port'] = rule.pop('port_range_min')
        rule['cidr_ip'] = rule.pop('remote_ip_prefix')
        rule['rule_type'] = rule.pop('direction')
    security_group['rules'] = sorted(rules)
    return security_group

def get_rules ( security_group , ** kwargs ) : rules = security_group . pop ( 'security_group_rules' , [ ] ) for rule in rules : rule [ 'ip_protocol' ] = rule . pop ( 'protocol' ) rule [ 'from_port' ] = rule . pop ( 'port_range_max' ) rule [ 'to_port' ] = rule . pop ( 'port_range_min' ) rule [ 'cidr_ip' ] = rule . pop ( 'remote_ip_prefix' ) rule [ 'rule_type' ] = rule . pop ( 'direction' ) security_group [ 'rules' ] = sorted ( rules ) return security_group

format the rule fields to match AWS to support auditor reuse will need to remap back if we want to orchestrate from our stored items


def get_security_group(security_group, flags=FLAGS.ALL, **kwargs):
    result = registry.build_out(flags, start_with=security_group,  pass_datastructure=True, **kwargs)
    """ just store the AWS formatted rules """
    result.pop('security_group_rules', [])
    return result

def get_security_group ( security_group , flags = FLAGS . ALL , ** kwargs ) : result = registry . build_out ( flags , start_with = security_group , pass_datastructure = True , ** kwargs ) result . pop ( 'security_group_rules' , [ ] ) return result


def get_inline_policies(group, **conn):
    """Get the inline policies for the group."""
    policy_list = list_group_policies(group['GroupName'])

    policy_documents = {}

    for policy in policy_list:
        policy_documents[policy] = get_group_policy_document(group['GroupName'], policy, **conn)

    return policy_documents

def get_inline_policies ( group , ** conn ) : policy_list = list_group_policies ( group [ 'GroupName' ] ) policy_documents = { } for policy in policy_list : policy_documents [ policy ] = get_group_policy_document ( group [ 'GroupName' ] , policy , ** conn ) return policy_documents


def get_managed_policies(group, **conn):
    """Get a list of the managed policy names that are attached to the group."""
    managed_policies = list_attached_group_managed_policies(group['GroupName'], **conn)

    managed_policy_names = []

    for policy in managed_policies:
        managed_policy_names.append(policy['PolicyName'])

    return managed_policy_names

def get_managed_policies ( group , ** conn ) : managed_policies = list_attached_group_managed_policies ( group [ 'GroupName' ] , ** conn ) managed_policy_names = [ ] for policy in managed_policies : managed_policy_names . append ( policy [ 'PolicyName' ] ) return managed_policy_names

Get a list of the managed policy names that are attached to the group .


def get_users(group, **conn):
    """Gets a list of the usernames that are a part of this group."""
    group_details = get_group_api(group['GroupName'], **conn)

    user_list = []
    for user in group_details.get('Users', []):
        user_list.append(user['UserName'])

    return user_list

def get_users ( group , ** conn ) : group_details = get_group_api ( group [ 'GroupName' ] , ** conn ) user_list = [ ] for user in group_details . get ( 'Users' , [ ] ) : user_list . append ( user [ 'UserName' ] ) return user_list

Gets a list of the usernames that are a part of this group .


def _get_base(group, **conn):
    """Fetch the base IAM Group."""
    group['_version'] = 1

    # Get the initial group details (only needed if we didn't grab the users):
    group.update(get_group_api(group['GroupName'], users=False, **conn)['Group'])

    # Cast CreateDate from a datetime to something JSON serializable.
    group['CreateDate'] = get_iso_string(group['CreateDate'])
    return group

def _get_base ( group , ** conn ) : group [ '_version' ] = 1 group . update ( get_group_api ( group [ 'GroupName' ] , users = False , ** conn ) [ 'Group' ] ) group [ 'CreateDate' ] = get_iso_string ( group [ 'CreateDate' ] ) return group


def get_base(managed_policy, **conn):
    """Fetch the base Managed Policy.

    This includes the base policy and the latest version document.

    :param managed_policy:
    :param conn:
    :return:
    """
    managed_policy['_version'] = 1

    arn = _get_name_from_structure(managed_policy, 'Arn')
    policy = get_policy(arn, **conn)
    document = get_managed_policy_document(arn, policy_metadata=policy, **conn)

    managed_policy.update(policy['Policy'])
    managed_policy['Document'] = document

    # Fix the dates:
    managed_policy['CreateDate'] = get_iso_string(managed_policy['CreateDate'])
    managed_policy['UpdateDate'] = get_iso_string(managed_policy['UpdateDate'])

    return managed_policy

def get_base ( managed_policy , ** conn ) : managed_policy [ '_version' ] = 1 arn = _get_name_from_structure ( managed_policy , 'Arn' ) policy = get_policy ( arn , ** conn ) document = get_managed_policy_document ( arn , policy_metadata = policy , ** conn ) managed_policy . update ( policy [ 'Policy' ] ) managed_policy [ 'Document' ] = document managed_policy [ 'CreateDate' ] = get_iso_string ( managed_policy [ 'CreateDate' ] ) managed_policy [ 'UpdateDate' ] = get_iso_string ( managed_policy [ 'UpdateDate' ] ) return managed_policy


def get_short_version(self):
        '''obtain the shory geoserver version
        '''
        gs_version = self.get_version()
        match = re.compile(r'[^\d.]+')
        return match.sub('', gs_version).strip('.')

def get_short_version ( self ) : gs_version = self . get_version ( ) match = re . compile ( r'[^\d.]+' ) return match . sub ( '' , gs_version ) . strip ( '.' )


def save(self, obj, content_type="application/xml"):
        """
        saves an object to the REST service
        gets the object's REST location and the data from the object,
        then POSTS the request.
        """
        rest_url = obj.href
        data = obj.message()

        headers = {
            "Content-type": content_type,
            "Accept": content_type
        }

        logger.debug("{} {}".format(obj.save_method, obj.href))
        resp = self.http_request(rest_url, method=obj.save_method.lower(), data=data, headers=headers)

        if resp.status_code not in (200, 201):
            raise FailedRequestError('Failed to save to Geoserver catalog: {}, {}'.format(resp.status_code, resp.text))

        self._cache.clear()
        return resp

saves an object to the REST service gets the object s REST location and the data from the object then POSTS the request .


def get_stores(self, names=None, workspaces=None):
        '''
          Returns a list of stores in the catalog. If workspaces is specified will only return stores in those workspaces.
          If names is specified, will only return stores that match.
          names can either be a comma delimited string or an array.
          Will return an empty list if no stores are found.
        '''

        if isinstance(workspaces, Workspace):
            workspaces = [workspaces]
        elif isinstance(workspaces, list) and [w for w in workspaces if isinstance(w, Workspace)]:
            # nothing
            pass
        else:
            workspaces = self.get_workspaces(names=workspaces)

        stores = []
        for ws in workspaces:
            ds_list = self.get_xml(ws.datastore_url)
            cs_list = self.get_xml(ws.coveragestore_url)
            wms_list = self.get_xml(ws.wmsstore_url)
            stores.extend([datastore_from_index(self, ws, n) for n in ds_list.findall("dataStore")])
            stores.extend([coveragestore_from_index(self, ws, n) for n in cs_list.findall("coverageStore")])
            stores.extend([wmsstore_from_index(self, ws, n) for n in wms_list.findall("wmsStore")])

        if names is None:
            names = []
        elif isinstance(names, basestring):
            names = [s.strip() for s in names.split(',') if s.strip()]

        if stores and names:
            return ([store for store in stores if store.name in names])

        return stores

def get_stores ( self , names = None , workspaces = None ) : if isinstance ( workspaces , Workspace ) : workspaces = [ workspaces ] elif isinstance ( workspaces , list ) and [ w for w in workspaces if isinstance ( w , Workspace ) ] : pass else : workspaces = self . get_workspaces ( names = workspaces ) stores = [ ] for ws in workspaces : ds_list = self . get_xml ( ws . datastore_url ) cs_list = self . get_xml ( ws . coveragestore_url ) wms_list = self . get_xml ( ws . wmsstore_url ) stores . extend ( [ datastore_from_index ( self , ws , n ) for n in ds_list . findall ( "dataStore" ) ] ) stores . extend ( [ coveragestore_from_index ( self , ws , n ) for n in cs_list . findall ( "coverageStore" ) ] ) stores . extend ( [ wmsstore_from_index ( self , ws , n ) for n in wms_list . findall ( "wmsStore" ) ] ) if names is None : names = [ ] elif isinstance ( names , basestring ) : names = [ s . strip ( ) for s in names . split ( ',' ) if s . strip ( ) ] if stores and names : return ( [ store for store in stores if store . name in names ] ) return stores

Returns a list of stores in the catalog . If workspaces is specified will only return stores in those workspaces . If names is specified will only return stores that match . names can either be a comma delimited string or an array . Will return an empty list if no stores are found .


def get_store(self, name, workspace=None):
        '''
          Returns a single store object.
          Will return None if no store is found.
          Will raise an error if more than one store with the same name is found.
        '''

        stores = self.get_stores(workspaces=workspace, names=name)
        return self._return_first_item(stores)

def get_store ( self , name , workspace = None ) : stores = self . get_stores ( workspaces = workspace , names = name ) return self . _return_first_item ( stores )

Returns a single store object . Will return None if no store is found . Will raise an error if more than one store with the same name is found .


def delete_granule(self, coverage, store, granule_id, workspace=None):
        '''Deletes a granule of an existing imagemosaic'''
        params = dict()

        workspace_name = workspace
        if isinstance(store, basestring):
            store_name = store
        else:
            store_name = store.name
            workspace_name = store.workspace.name

        if workspace_name is None:
            raise ValueError("Must specify workspace")

        url = build_url(
            self.service_url,
            [
                "workspaces",
                workspace_name,
                "coveragestores",
                store_name,
                "coverages",
                coverage,
                "index/granules",
                granule_id,
                ".json"
            ],
            params
        )

        # DELETE /workspaces/<ws>/coveragestores/<name>/coverages/<coverage>/index/granules/<granule_id>.json
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json"
        }

        resp = self.http_request(url, method='delete', headers=headers)
        if resp.status_code != 200:
            FailedRequestError('Failed to delete granule from mosaic {} : {}, {}'.format(store, resp.status_code, resp.text))
        self._cache.clear()

        # maybe return a list of all granules?
        return None


def list_granules(self, coverage, store, workspace=None, filter=None, limit=None, offset=None):
        '''List granules of an imagemosaic'''
        params = dict()

        if filter is not None:
            params['filter'] = filter
        if limit is not None:
            params['limit'] = limit
        if offset is not None:
            params['offset'] = offset

        workspace_name = workspace
        if isinstance(store, basestring):
            store_name = store
        else:
            store_name = store.name
            workspace_name = store.workspace.name

        if workspace_name is None:
            raise ValueError("Must specify workspace")

        url = build_url(
            self.service_url,
            [
                "workspaces",
                workspace_name,
                "coveragestores",
                store_name,
                "coverages",
                coverage,
                "index/granules.json"
            ],
            params
        )

        # GET /workspaces/<ws>/coveragestores/<name>/coverages/<coverage>/index/granules.json
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json"
        }

        resp = self.http_request(url, headers=headers)
        if resp.status_code != 200:
            FailedRequestError('Failed to list granules in mosaic {} : {}, {}'.format(store, resp.status_code, resp.text))

        self._cache.clear()
        return resp.json()


def mosaic_coverages(self, store):
        '''Returns all coverages in a coverage store'''
        params = dict()
        url = build_url(
            self.service_url,
            [
                "workspaces",
                store.workspace.name,
                "coveragestores",
                store.name,
                "coverages.json"
            ],
            params
        )
        # GET /workspaces/<ws>/coveragestores/<name>/coverages.json
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json"
        }

        resp = self.http_request(url, headers=headers)
        if resp.status_code != 200:
            FailedRequestError('Failed to get mosaic coverages {} : {}, {}'.format(store, resp.status_code, resp.text))

        self._cache.clear()
        return resp.json()


def publish_featuretype(self, name, store, native_crs, srs=None, jdbc_virtual_table=None, native_name=None):
        '''Publish a featuretype from data in an existing store'''
        # @todo native_srs doesn't seem to get detected, even when in the DB
        # metadata (at least for postgis in geometry_columns) and then there
        # will be a misconfigured layer
        if native_crs is None:
            raise ValueError("must specify native_crs")

        srs = srs or native_crs
        feature_type = FeatureType(self, store.workspace, store, name)
        # because name is the in FeatureType base class, work around that
        # and hack in these others that don't have xml properties
        feature_type.dirty['name'] = name
        feature_type.dirty['srs'] = srs
        feature_type.dirty['nativeCRS'] = native_crs
        feature_type.enabled = True
        feature_type.advertised = True
        feature_type.title = name

        if native_name is not None:
            feature_type.native_name = native_name

        headers = {
            "Content-type": "application/xml",
            "Accept": "application/xml"
        }

        resource_url = store.resource_url
        if jdbc_virtual_table is not None:
            feature_type.metadata = ({'JDBC_VIRTUAL_TABLE': jdbc_virtual_table})
            params = dict()
            resource_url = build_url(
                self.service_url,
                [
                    "workspaces",
                    store.workspace.name,
                    "datastores", store.name,
                    "featuretypes.xml"
                ],
                params
            )

        resp = self.http_request(resource_url, method='post', data=feature_type.message(), headers=headers)
        if resp.status_code not in (200, 201, 202):
            FailedRequestError('Failed to publish feature type {} : {}, {}'.format(name, resp.status_code, resp.text))

        self._cache.clear()
        feature_type.fetch()
        return feature_type


def get_resources(self, names=None, stores=None, workspaces=None):
        '''
        Resources include feature stores, coverage stores and WMS stores, however does not include layer groups.
        names, stores and workspaces can be provided as a comma delimited strings or as arrays, and are used for filtering.
        Will always return an array.
        '''

        stores = self.get_stores(
            names = stores,
            workspaces = workspaces
        )

        resources = []
        for s in stores:
            try:
                resources.extend(s.get_resources())
            except FailedRequestError:
                continue

        if names is None:
            names = []
        elif isinstance(names, basestring):
            names = [s.strip() for s in names.split(',') if s.strip()]

        if resources and names:
            return ([resource for resource in resources if resource.name in names])

        return resources

def get_resources ( self , names = None , stores = None , workspaces = None ) : stores = self . get_stores ( names = stores , workspaces = workspaces ) resources = [ ] for s in stores : try : resources . extend ( s . get_resources ( ) ) except FailedRequestError : continue if names is None : names = [ ] elif isinstance ( names , basestring ) : names = [ s . strip ( ) for s in names . split ( ',' ) if s . strip ( ) ] if resources and names : return ( [ resource for resource in resources if resource . name in names ] ) return resources

Resources include feature stores coverage stores and WMS stores however does not include layer groups . names stores and workspaces can be provided as a comma delimited strings or as arrays and are used for filtering . Will always return an array .


def get_resource(self, name=None, store=None, workspace=None):
        '''
          returns a single resource object.
          Will return None if no resource is found.
          Will raise an error if more than one resource with the same name is found.
        '''

        resources = self.get_resources(names=name, stores=store, workspaces=workspace)
        return self._return_first_item(resources)

def get_resource ( self , name = None , store = None , workspace = None ) : resources = self . get_resources ( names = name , stores = store , workspaces = workspace ) return self . _return_first_item ( resources )

returns a single resource object . Will return None if no resource is found . Will raise an error if more than one resource with the same name is found .


def get_layergroup(self, name, workspace=None):
        '''
          returns a single layergroup object.
          Will return None if no layergroup is found.
          Will raise an error if more than one layergroup with the same name is found.
        '''

        layergroups = self.get_layergroups(names=name, workspaces=workspace)
        return self._return_first_item(layergroups)

def get_layergroup ( self , name , workspace = None ) : layergroups = self . get_layergroups ( names = name , workspaces = workspace ) return self . _return_first_item ( layergroups )

returns a single layergroup object . Will return None if no layergroup is found . Will raise an error if more than one layergroup with the same name is found .


def get_style(self, name, workspace=None):
        '''
          returns a single style object.
          Will return None if no style is found.
          Will raise an error if more than one style with the same name is found.
        '''

        styles = self.get_styles(names=name, workspaces=workspace)
        return self._return_first_item(styles)

def get_style ( self , name , workspace = None ) : styles = self . get_styles ( names = name , workspaces = workspace ) return self . _return_first_item ( styles )

returns a single style object . Will return None if no style is found . Will raise an error if more than one style with the same name is found .


def get_workspaces(self, names=None):
        '''
          Returns a list of workspaces in the catalog.
          If names is specified, will only return workspaces that match.
          names can either be a comma delimited string or an array.
          Will return an empty list if no workspaces are found.
        '''
        if names is None:
            names = []
        elif isinstance(names, basestring):
            names = [s.strip() for s in names.split(',') if s.strip()]

        data = self.get_xml("{}/workspaces.xml".format(self.service_url))
        workspaces = []
        workspaces.extend([workspace_from_index(self, node) for node in data.findall("workspace")])

        if workspaces and names:
            return ([ws for ws in workspaces if ws.name in names])

        return workspaces

def get_workspaces ( self , names = None ) : if names is None : names = [ ] elif isinstance ( names , basestring ) : names = [ s . strip ( ) for s in names . split ( ',' ) if s . strip ( ) ] data = self . get_xml ( "{}/workspaces.xml" . format ( self . service_url ) ) workspaces = [ ] workspaces . extend ( [ workspace_from_index ( self , node ) for node in data . findall ( "workspace" ) ] ) if workspaces and names : return ( [ ws for ws in workspaces if ws . name in names ] ) return workspaces

Returns a list of workspaces in the catalog . If names is specified will only return workspaces that match . names can either be a comma delimited string or an array . Will return an empty list if no workspaces are found .


def get_workspace(self, name):
        '''
          returns a single workspace object.
          Will return None if no workspace is found.
          Will raise an error if more than one workspace with the same name is found.
        '''

        workspaces = self.get_workspaces(names=name)
        return self._return_first_item(workspaces)

def get_workspace ( self , name ) : workspaces = self . get_workspaces ( names = name ) return self . _return_first_item ( workspaces )

returns a single workspace object . Will return None if no workspace is found . Will raise an error if more than one workspace with the same name is found .


def md_link(node):
    """Extract a metadata link tuple from an xml node"""
    mimetype = node.find("type")
    mdtype = node.find("metadataType")
    content = node.find("content")
    if None in [mimetype, mdtype, content]:
        return None
    else:
        return (mimetype.text, mdtype.text, content.text)

def md_link ( node ) : mimetype = node . find ( "type" ) mdtype = node . find ( "metadataType" ) content = node . find ( "content" ) if None in [ mimetype , mdtype , content ] : return None else : return ( mimetype . text , mdtype . text , content . text )


def build_url(base, seg, query=None):
    """
    Create a URL from a list of path segments and an optional dict of query
    parameters.
    """

    def clean_segment(segment):
        """
        Cleans the segment and encodes to UTF-8 if the segment is unicode.
        """
        segment = segment.strip('/')
        if isinstance(segment, basestring):
            segment = segment.encode('utf-8')
        return segment

    seg = (quote(clean_segment(s)) for s in seg)
    if query is None or len(query) == 0:
        query_string = ''
    else:
        query_string = "?" + urlencode(query)
    path = '/'.join(seg) + query_string
    adjusted_base = base.rstrip('/') + '/'
    return urljoin(str(adjusted_base), str(path))

def build_url ( base , seg , query = None ) : def clean_segment ( segment ) : segment = segment . strip ( '/' ) if isinstance ( segment , basestring ) : segment = segment . encode ( 'utf-8' ) return segment seg = ( quote ( clean_segment ( s ) ) for s in seg ) if query is None or len ( query ) == 0 : query_string = '' else : query_string = "?" + urlencode ( query ) path = '/' . join ( seg ) + query_string adjusted_base = base . rstrip ( '/' ) + '/' return urljoin ( str ( adjusted_base ) , str ( path ) )

Create a URL from a list of path segments and an optional dict of query parameters .


def prepare_upload_bundle(name, data):
    """GeoServer's REST API uses ZIP archives as containers for file formats such
    as Shapefile and WorldImage which include several 'boxcar' files alongside
    the main data.  In such archives, GeoServer assumes that all of the relevant
    files will have the same base name and appropriate extensions, and live in
    the root of the ZIP archive.  This method produces a zip file that matches
    these expectations, based on a basename, and a dict of extensions to paths or
    file-like objects. The client code is responsible for deleting the zip
    archive when it's done."""
    fd, path = mkstemp()
    zip_file = ZipFile(path, 'w')
    for ext, stream in data.items():
        fname = "%s.%s" % (name, ext)
        if (isinstance(stream, basestring)):
            zip_file.write(stream, fname)
        else:
            zip_file.writestr(fname, stream.read())
    zip_file.close()
    os.close(fd)
    return path

def prepare_upload_bundle ( name , data ) : fd , path = mkstemp ( ) zip_file = ZipFile ( path , 'w' ) for ext , stream in data . items ( ) : fname = "%s.%s" % ( name , ext ) if ( isinstance ( stream , basestring ) ) : zip_file . write ( stream , fname ) else : zip_file . writestr ( fname , stream . read ( ) ) zip_file . close ( ) os . close ( fd ) return path

GeoServer s REST API uses ZIP archives as containers for file formats such as Shapefile and WorldImage which include several boxcar files alongside the main data . In such archives GeoServer assumes that all of the relevant files will have the same base name and appropriate extensions and live in the root of the ZIP archive . This method produces a zip file that matches these expectations based on a basename and a dict of extensions to paths or file - like objects . The client code is responsible for deleting the zip archive when it s done .


def md_dimension_info(name, node):
    """Extract metadata Dimension Info from an xml node"""
    def _get_value(child_name):
        return getattr(node.find(child_name), 'text', None)

    resolution = _get_value('resolution')
    defaultValue = node.find("defaultValue")
    strategy = defaultValue.find("strategy") if defaultValue is not None else None
    strategy = strategy.text if strategy is not None else None
    return DimensionInfo(
        name,
        _get_value('enabled') == 'true',
        _get_value('presentation'),
        int(resolution) if resolution else None,
        _get_value('units'),
        _get_value('unitSymbol'),
        strategy,
        _get_value('attribute'),
        _get_value('endAttribute'),
        _get_value('referenceValue'),
        _get_value('nearestMatchEnabled')
    )

def md_dimension_info ( name , node ) : def _get_value ( child_name ) : return getattr ( node . find ( child_name ) , 'text' , None ) resolution = _get_value ( 'resolution' ) defaultValue = node . find ( "defaultValue" ) strategy = defaultValue . find ( "strategy" ) if defaultValue is not None else None strategy = strategy . text if strategy is not None else None return DimensionInfo ( name , _get_value ( 'enabled' ) == 'true' , _get_value ( 'presentation' ) , int ( resolution ) if resolution else None , _get_value ( 'units' ) , _get_value ( 'unitSymbol' ) , strategy , _get_value ( 'attribute' ) , _get_value ( 'endAttribute' ) , _get_value ( 'referenceValue' ) , _get_value ( 'nearestMatchEnabled' ) )


def md_dynamic_default_values_info(name, node):
    """Extract metadata Dynamic Default Values from an xml node"""
    configurations = node.find("configurations")
    if configurations is not None:
        configurations = []
        for n in node.findall("configuration"):
            dimension = n.find("dimension")
            dimension = dimension.text if dimension is not None else None
            policy = n.find("policy")
            policy = policy.text if policy is not None else None
            defaultValueExpression = n.find("defaultValueExpression")
            defaultValueExpression = defaultValueExpression.text if defaultValueExpression is not None else None

            configurations.append(DynamicDefaultValuesConfiguration(dimension, policy, defaultValueExpression))

    return DynamicDefaultValues(name, configurations)

def md_dynamic_default_values_info ( name , node ) : configurations = node . find ( "configurations" ) if configurations is not None : configurations = [ ] for n in node . findall ( "configuration" ) : dimension = n . find ( "dimension" ) dimension = dimension . text if dimension is not None else None policy = n . find ( "policy" ) policy = policy . text if policy is not None else None defaultValueExpression = n . find ( "defaultValueExpression" ) defaultValueExpression = defaultValueExpression . text if defaultValueExpression is not None else None configurations . append ( DynamicDefaultValuesConfiguration ( dimension , policy , defaultValueExpression ) ) return DynamicDefaultValues ( name , configurations )


def md_jdbc_virtual_table(key, node):
    """Extract metadata JDBC Virtual Tables from an xml node"""
    name = node.find("name")
    sql = node.find("sql")
    escapeSql = node.find("escapeSql")
    escapeSql = escapeSql.text if escapeSql is not None else None
    keyColumn = node.find("keyColumn")
    keyColumn = keyColumn.text if keyColumn is not None else None
    n_g = node.find("geometry")
    geometry = JDBCVirtualTableGeometry(n_g.find("name"), n_g.find("type"), n_g.find("srid"))
    parameters = []
    for n_p in node.findall("parameter"):
        p_name = n_p.find("name")
        p_defaultValue = n_p.find("defaultValue")
        p_defaultValue = p_defaultValue.text if p_defaultValue is not None else None
        p_regexpValidator = n_p.find("regexpValidator")
        p_regexpValidator = p_regexpValidator.text if p_regexpValidator is not None else None
        parameters.append(JDBCVirtualTableParam(p_name, p_defaultValue, p_regexpValidator))

    return JDBCVirtualTable(name, sql, escapeSql, geometry, keyColumn, parameters)

def md_jdbc_virtual_table ( key , node ) : name = node . find ( "name" ) sql = node . find ( "sql" ) escapeSql = node . find ( "escapeSql" ) escapeSql = escapeSql . text if escapeSql is not None else None keyColumn = node . find ( "keyColumn" ) keyColumn = keyColumn . text if keyColumn is not None else None n_g = node . find ( "geometry" ) geometry = JDBCVirtualTableGeometry ( n_g . find ( "name" ) , n_g . find ( "type" ) , n_g . find ( "srid" ) ) parameters = [ ] for n_p in node . findall ( "parameter" ) : p_name = n_p . find ( "name" ) p_defaultValue = n_p . find ( "defaultValue" ) p_defaultValue = p_defaultValue . text if p_defaultValue is not None else None p_regexpValidator = n_p . find ( "regexpValidator" ) p_regexpValidator = p_regexpValidator . text if p_regexpValidator is not None else None parameters . append ( JDBCVirtualTableParam ( p_name , p_defaultValue , p_regexpValidator ) ) return JDBCVirtualTable ( name , sql , escapeSql , geometry , keyColumn , parameters )


def md_entry(node):
    """Extract metadata entries from an xml node"""
    key = None
    value = None
    if 'key' in node.attrib:
        key = node.attrib['key']
    else:
        key = None

    if key in ['time', 'elevation'] or key.startswith('custom_dimension'):
        value = md_dimension_info(key, node.find("dimensionInfo"))
    elif key == 'DynamicDefaultValues':
        value = md_dynamic_default_values_info(key, node.find("DynamicDefaultValues"))
    elif key == 'JDBC_VIRTUAL_TABLE':
        value = md_jdbc_virtual_table(key, node.find("virtualTable"))
    else:
        value = node.text

    if None in [key, value]:
        return None
    else:
        return (key, value)

def md_entry ( node ) : key = None value = None if 'key' in node . attrib : key = node . attrib [ 'key' ] else : key = None if key in [ 'time' , 'elevation' ] or key . startswith ( 'custom_dimension' ) : value = md_dimension_info ( key , node . find ( "dimensionInfo" ) ) elif key == 'DynamicDefaultValues' : value = md_dynamic_default_values_info ( key , node . find ( "DynamicDefaultValues" ) ) elif key == 'JDBC_VIRTUAL_TABLE' : value = md_jdbc_virtual_table ( key , node . find ( "virtualTable" ) ) else : value = node . text if None in [ key , value ] : return None else : return ( key , value )


def resolution_millis(self):
        '''if set, get the value of resolution in milliseconds'''
        if self.resolution is None or not isinstance(self.resolution, basestring):
                return self.resolution
        val, mult = self.resolution.split(' ')
        return int(float(val) * self._multipier(mult) * 1000)

def
resolution_millis
(
self
)
:
if
self
.
resolution
is
or
not
isinstance
(
self
.
resolution
,
basestring
)
:
return
self
.
resolution
val
,
mult
=
self
.
resolution
.
split
(
' '
)
return
int
(
float
(
val
)
*
self
.
_multipier
(
mult
)
*
1000
)

if set get the value of resolution in milliseconds


def _init(self):
        """Read resource information into self._cache, for cached access.

        See DAVResource._init()
        """
        # TODO: recalc self.path from <self._file_path>, to fix correct file system case
        #       On windows this would lead to correct URLs
        self.provider._count_get_resource_inst_init += 1
        tableName, primKey = self.provider._split_path(self.path)

        display_type = "Unknown"
        displayTypeComment = ""
        contentType = "text/html"

        #        _logger.debug("getInfoDict(%s), nc=%s" % (path, self.connectCount))
        if tableName is None:
            display_type = "Database"
        elif primKey is None:  # "database" and table name
            display_type = "Database Table"
        else:
            contentType = "text/csv"
            if primKey == "_ENTIRE_CONTENTS":
                display_type = "Database Table Contents"
                displayTypeComment = "CSV Representation of Table Contents"
            else:
                display_type = "Database Record"
                displayTypeComment = "Attributes available as properties"

        # Avoid calling is_collection, since it would call isExisting -> _init_connection
        is_collection = primKey is None

        self._cache = {
            "content_length": None,
            "contentType": contentType,
            "created": time.time(),
            "display_name": self.name,
            "etag": hashlib.md5().update(self.path).hexdigest(),
            # "etag": md5.new(self.path).hexdigest(),
            "modified": None,
            "support_ranges": False,
            "display_info": {"type": display_type, "typeComment": displayTypeComment},
        }

        # Some resource-only infos:
        if not is_collection:
            self._cache["modified"] = time.time()
        _logger.debug("---> _init, nc=%s" % self.provider._count_initConnection)

def
_init
(
self
)
:
self
.
provider
.
_count_get_resource_inst_init
+=
1
tableName
,
primKey
=
self
.
provider
.
_split_path
(
self
.
path
)
display_type
=
"Unknown"
displayTypeComment
=
""
contentType
=
"text/html"
if
tableName
is
:
display_type
=
"Database"
elif
primKey
is
:
display_type
=
"Database Table"
else
:
contentType
=
"text/csv"
if
primKey
==
"_ENTIRE_CONTENTS"
:
display_type
=
"Database Table Contents"
displayTypeComment
=
"CSV Representation of Table Contents"
else
:
display_type
=
"Database Record"
displayTypeComment
=
"Attributes available as properties"
is_collection
=
primKey
is
self
.
_cache
=
{
"content_length"
:
,
"contentType"
:
contentType
,
"created"
:
time
.
time
(
)
,
"display_name"
:
self
.
name
,
"etag"
:
hashlib
.
md5
(
)
.
update
(
self
.
path
)
.
hexdigest
(
)
,
"modified"
:
,
"support_ranges"
:
False
,
"display_info"
:
{
"type"
:
display_type
,
"typeComment"
:
displayTypeComment
}
,
}
if
not
is_collection
:
self
.
_cache
[
"modified"
]
=
time
.
time
(
)
_logger
.
debug
(
"-
%
self
.
provider
.
_count_initConnection
)

Read resource information into self . _cache for cached access .


def as_DAVError(e):
    """Convert any non-DAVError exception to HTTP_INTERNAL_ERROR."""
    if isinstance(e, DAVError):
        return e
    elif isinstance(e, Exception):
        # traceback.print_exc()
        return DAVError(HTTP_INTERNAL_ERROR, src_exception=e)
    else:
        return DAVError(HTTP_INTERNAL_ERROR, "{}".format(e))

def as_DAVError ( e ) : if isinstance ( e , DAVError ) : return e elif isinstance ( e , Exception ) : return DAVError ( HTTP_INTERNAL_ERROR , src_exception = e ) else : return DAVError ( HTTP_INTERNAL_ERROR , "{}" . format ( e ) )

Convert any non - DAVError exception to HTTP_INTERNAL_ERROR .


def get_user_info(self):
        """Return readable string."""
        if self.value in ERROR_DESCRIPTIONS:
            s = "{}".format(ERROR_DESCRIPTIONS[self.value])
        else:
            s = "{}".format(self.value)

        if self.context_info:
            s += ": {}".format(self.context_info)
        elif self.value in ERROR_RESPONSES:
            s += ": {}".format(ERROR_RESPONSES[self.value])

        if self.src_exception:
            s += "\n    Source exception: '{}'".format(self.src_exception)

        if self.err_condition:
            s += "\n    Error condition: '{}'".format(self.err_condition)
        return s

def
get_user_info
(
self
)
:
if
self
.
value
in
ERROR_DESCRIPTIONS
:
s
=
"{}"
.
format
(
ERROR_DESCRIPTIONS
[
self
.
value
]
)
else
:
s
=
"{}"
.
format
(
self
.
value
)
if
self
.
context_info
:
s
+=
": {}"
.
format
(
self
.
context_info
)
elif
self
.
value
in
ERROR_RESPONSES
:
s
+=
": {}"
.
format
(
ERROR_RESPONSES
[
self
.
value
]
)
if
self
.
src_exception
:
s
+=
"\n    Source exception: '{}'"
.
format
(
self
.
src_exception
)
if
self
.
err_condition
:
s
+=
"\n    Error condition: '{}'"
.
format
(
self
.
err_condition
)
return
s


def handle_delete(self):
        """Change semantic of DELETE to remove resource tags."""
        # DELETE is only supported for the '/by_tag/' collection
        if "/by_tag/" not in self.path:
            raise DAVError(HTTP_FORBIDDEN)
        # path must be '/by_tag/<tag>/<resname>'
        catType, tag, _rest = util.save_split(self.path.strip("/"), "/", 2)
        assert catType == "by_tag"
        assert tag in self.data["tags"]
        self.data["tags"].remove(tag)
        return True

def handle_delete ( self ) : if "/by_tag/" not in self . path : raise DAVError ( HTTP_FORBIDDEN ) catType , tag , _rest = util . save_split ( self . path . strip ( "/" ) , "/" , 2 ) assert catType == "by_tag" assert tag in self . data [ "tags" ] self . data [ "tags" ] . remove ( tag ) return True

Change semantic of DELETE to remove resource tags .


def handle_copy(self, dest_path, depth_infinity):
        """Change semantic of COPY to add resource tags."""
        # destPath must be '/by_tag/<tag>/<resname>'
        if "/by_tag/" not in dest_path:
            raise DAVError(HTTP_FORBIDDEN)
        catType, tag, _rest = util.save_split(dest_path.strip("/"), "/", 2)
        assert catType == "by_tag"
        if tag not in self.data["tags"]:
            self.data["tags"].append(tag)
        return True

def handle_copy ( self , dest_path , depth_infinity ) : if "/by_tag/" not in dest_path : raise DAVError ( HTTP_FORBIDDEN ) catType , tag , _rest = util . save_split ( dest_path . strip ( "/" ) , "/" , 2 ) assert catType == "by_tag" if tag not in self . data [ "tags" ] : self . data [ "tags" ] . append ( tag ) return True

Change semantic of COPY to add resource tags .


def handle_move(self, dest_path):
        """Change semantic of MOVE to change resource tags."""
        # path and destPath must be '/by_tag/<tag>/<resname>'
        if "/by_tag/" not in self.path:
            raise DAVError(HTTP_FORBIDDEN)
        if "/by_tag/" not in dest_path:
            raise DAVError(HTTP_FORBIDDEN)
        catType, tag, _rest = util.save_split(self.path.strip("/"), "/", 2)
        assert catType == "by_tag"
        assert tag in self.data["tags"]
        self.data["tags"].remove(tag)
        catType, tag, _rest = util.save_split(dest_path.strip("/"), "/", 2)
        assert catType == "by_tag"
        if tag not in self.data["tags"]:
            self.data["tags"].append(tag)
        return True

def handle_move ( self , dest_path ) : if "/by_tag/" not in self . path : raise DAVError ( HTTP_FORBIDDEN ) if "/by_tag/" not in dest_path : raise DAVError ( HTTP_FORBIDDEN ) catType , tag , _rest = util . save_split ( self . path . strip ( "/" ) , "/" , 2 ) assert catType == "by_tag" assert tag in self . data [ "tags" ] self . data [ "tags" ] . remove ( tag ) catType , tag , _rest = util . save_split ( dest_path . strip ( "/" ) , "/" , 2 ) assert catType == "by_tag" if tag not in self . data [ "tags" ] : self . data [ "tags" ] . append ( tag ) return True

Change semantic of MOVE to change resource tags .


def get_resource_inst(self, path, environ):
        """Return _VirtualResource object for path.

        path is expected to be
            categoryType/category/name/artifact
        for example:
            'by_tag/cool/My doc 2/info.html'

        See DAVProvider.get_resource_inst()
        """
        _logger.info("get_resource_inst('%s')" % path)
        self._count_get_resource_inst += 1
        root = RootCollection(environ)
        return root.resolve("", path)

def get_resource_inst ( self , path , environ ) : _logger . info ( "get_resource_inst('%s')" % path ) self . _count_get_resource_inst += 1 root = RootCollection ( environ ) return root . resolve ( "" , path )

Return _VirtualResource object for path .


def add_provider(self, share, provider, readonly=False):
        """Add a provider to the provider_map routing table."""
        # Make sure share starts with, or is '/'
        share = "/" + share.strip("/")
        assert share not in self.provider_map

        if compat.is_basestring(provider):
            # Syntax:
            #   <mount_path>: <folder_path>
            # We allow a simple string as 'provider'. In this case we interpret
            # it as a file system root folder that is published.
            provider = FilesystemProvider(provider, readonly)
        elif type(provider) in (dict,):
            if "provider" in provider:
                # Syntax:
                #   <mount_path>: {"provider": <class_path>, "args": <pos_args>, "kwargs": <named_args}
                prov_class = dynamic_import_class(provider["provider"])
                provider = prov_class(
                    *provider.get("args", []), **provider.get("kwargs", {})
                )
            else:
                # Syntax:
                #   <mount_path>: {"root": <path>, "redaonly": <bool>}
                provider = FilesystemProvider(
                    provider["root"], bool(provider.get("readonly", False))
                )
        elif type(provider) in (list, tuple):
            raise ValueError(
                "Provider {}: tuple/list syntax is no longer supported".format(provider)
            )
            # provider = FilesystemProvider(provider[0], provider[1])

        if not isinstance(provider, DAVProvider):
            raise ValueError("Invalid provider {}".format(provider))

        provider.set_share_path(share)
        if self.mount_path:
            provider.set_mount_path(self.mount_path)

        # TODO: someday we may want to configure different lock/prop
        # managers per provider
        provider.set_lock_manager(self.lock_manager)
        provider.set_prop_manager(self.prop_manager)

        self.provider_map[share] = provider
        # self.provider_map[share] = {"provider": provider, "allow_anonymous": False}

        # Store the list of share paths, ordered by length, so route lookups
        # will return the most specific match
        self.sorted_share_list = [s.lower() for s in self.provider_map.keys()]
        self.sorted_share_list = sorted(self.sorted_share_list, key=len, reverse=True)

        return provider

Add a provider to the provider_map routing table .


def resolve_provider(self, path):
        """Get the registered DAVProvider for a given path.

        Returns:
            tuple: (share, provider)
        """
        # Find DAV provider that matches the share
        share = None
        lower_path = path.lower()
        for r in self.sorted_share_list:
            # @@: Case sensitivity should be an option of some sort here;
            # os.path.normpath might give the preferred case for a filename.
            if r == "/":
                share = r
                break
            elif lower_path == r or lower_path.startswith(r + "/"):
                share = r
                break

        if share is None:
            return None, None
        return share, self.provider_map.get(share)

def resolve_provider ( self , path ) : share = None lower_path = path . lower ( ) for r in self . sorted_share_list : if r == "/" : share = r break elif lower_path == r or lower_path . startswith ( r + "/" ) : share = r break if share is None : return None , None return share , self . provider_map . get ( share )

Get the registered DAVProvider for a given path .


def compute_digest_response(
        self, realm, user_name, method, uri, nonce, cnonce, qop, nc, environ
    ):
        """Computes digest hash.

        Calculation of the A1 (HA1) part is delegated to the dc interface method
        `digest_auth_user()`.

        Args:
            realm (str):
            user_name (str):
            method (str): WebDAV Request Method
            uri (str):
            nonce (str): server generated nonce value
            cnonce (str): client generated cnonce value
            qop (str): quality of protection
            nc (str) (number), nonce counter incremented by client
        Returns:
            MD5 hash string
            or False if user rejected by domain controller
        """

        def md5h(data):
            return md5(compat.to_bytes(data)).hexdigest()

        def md5kd(secret, data):
            return md5h(secret + ":" + data)

        A1 = self.domain_controller.digest_auth_user(realm, user_name, environ)
        if not A1:
            return False

        A2 = method + ":" + uri

        if qop:
            res = md5kd(
                A1, nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + md5h(A2)
            )
        else:
            res = md5kd(A1, nonce + ":" + md5h(A2))

        return res

def compute_digest_response ( self , realm , user_name , method , uri , nonce , cnonce , qop , nc , environ ) : def md5h ( data ) : return md5 ( compat . to_bytes ( data ) ) . hexdigest ( ) def md5kd ( secret , data ) : return md5h ( secret + ":" + data ) A1 = self . domain_controller . digest_auth_user ( realm , user_name , environ ) if not A1 : return False A2 = method + ":" + uri if qop : res = md5kd ( A1 , nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + md5h ( A2 ) ) else : res = md5kd ( A1 , nonce + ":" + md5h ( A2 ) ) return res


def read(self, size=0):
        """Read a chunk of bytes from queue.

        size = 0: Read next chunk (arbitrary length)
             > 0: Read one chunk of `size` bytes (or less if stream was closed)
             < 0: Read all bytes as single chunk (i.e. blocks until stream is closed)

        This method blocks until the requested size become available.
        However, if close() was called, '' is returned immediately.
        """
        res = self.unread
        self.unread = ""
        # Get next chunk, cumulating requested size as needed
        while res == "" or size < 0 or (size > 0 and len(res) < size):
            try:
                # Read pending data, blocking if neccessary
                # (but handle the case that close() is called while waiting)
                res += compat.to_native(self.queue.get(True, 0.1))
            except compat.queue.Empty:
                # There was no pending data: wait for more, unless close() was called
                if self.is_closed:
                    break
        # Deliver `size` bytes from buffer
        if size > 0 and len(res) > size:
            self.unread = res[size:]
            res = res[:size]
        # print("FileLikeQueue.read({}) => {} bytes".format(size, len(res)))
        return res

def read ( self , size = 0 ) : res = self . unread self . unread = "" while res == "" or size < 0 or ( size > 0 and len ( res ) < size ) : try : res += compat . to_native ( self . queue . get ( True , 0.1 ) ) except compat . queue . Empty : if self . is_closed : break if size > 0 and len ( res ) > size : self . unread = res [ size : ] res = res [ : size ] return res


def read(self, size=None):
        """Read bytes from an iterator."""
        while size is None or len(self.buffer) < size:
            try:
                self.buffer += next(self.data_stream)
            except StopIteration:
                break

        sized_chunk = self.buffer[:size]
        if size is None:
            self.buffer = ""
        else:
            self.buffer = self.buffer[size:]
        return sized_chunk

def read ( self , size = None ) : while size is None or len ( self . buffer ) < size : try : self . buffer += next ( self . data_stream ) except StopIteration : break sized_chunk = self . buffer [ : size ] if size is None : self . buffer = "" else : self . buffer = self . buffer [ size : ] return sized_chunk


def handle_error(self, request, client_address):
        """Handle an error gracefully.  May be overridden.

        The default is to _logger.info a traceback and continue.

        """
        ei = sys.exc_info()
        e = ei[1]
        # Suppress stack trace when client aborts connection disgracefully:
        # 10053: Software caused connection abort
        # 10054: Connection reset by peer
        if e.args[0] in (10053, 10054):
            _logger.error("*** Caught socket.error: {}".format(e))
            return
        # This is what BaseHTTPServer.HTTPServer.handle_error does, but with
        # added thread ID and using stderr
        _logger.error("-" * 40, file=sys.stderr)
        _logger.error(
            "<{}> Exception happened during processing of request from {}".format(
                threading.currentThread().ident, client_address
            )
        )
        _logger.error(client_address, file=sys.stderr)
        traceback.print_exc()
        _logger.error("-" * 40, file=sys.stderr)
        _logger.error(request, file=sys.stderr)

Handle an error gracefully . May be overridden .


def end_write(self, with_errors):
        """Called when PUT has finished writing.

        See DAVResource.end_write()
        """
        if not with_errors:
            commands.add(self.provider.ui, self.provider.repo, self.localHgPath)

def end_write ( self , with_errors ) : if not with_errors : commands . add ( self . provider . ui , self . provider . repo , self . localHgPath )


def handle_copy(self, dest_path, depth_infinity):
        """Handle a COPY request natively.

        """
        destType, destHgPath = util.pop_path(dest_path)
        destHgPath = destHgPath.strip("/")
        ui = self.provider.ui
        repo = self.provider.repo
        _logger.info("handle_copy %s -> %s" % (self.localHgPath, destHgPath))
        if self.rev is None and destType == "edit":
            # COPY /edit/a/b to /edit/c/d: turn into 'hg copy -f a/b c/d'
            commands.copy(ui, repo, self.localHgPath, destHgPath, force=True)
        elif self.rev is None and destType == "released":
            # COPY /edit/a/b to /released/c/d
            # This is interpreted as 'hg commit a/b' (ignoring the dest. path)
            self._commit("WsgiDAV commit (COPY %s -> %s)" % (self.path, dest_path))
        else:
            raise DAVError(HTTP_FORBIDDEN)
        # Return True: request was handled
        return True

def
handle_copy
(
self
,
dest_path
,
depth_infinity
)
:
destType
,
destHgPath
=
util
.
pop_path
(
dest_path
)
destHgPath
=
destHgPath
.
strip
(
"/"
)
ui
=
self
.
provider
.
ui
repo
=
self
.
provider
.
repo
_logger
.
info
(
"handle_copy %s -> %s"
%
(
self
.
localHgPath
,
destHgPath
)
)
if
self
.
rev
is
and
destType
==
"edit"
:
commands
.
copy
(
ui
,
repo
,
self
.
localHgPath
,
destHgPath
,
force
=
True
)
elif
self
.
rev
is
and
destType
==
"released"
:
self
.
_commit
(
"WsgiDAV commit (COPY %s -> %s)"
%
(
self
.
path
,
dest_path
)
)
else
:
raise
DAVError
(
HTTP_FORBIDDEN
)
return
True


def _get_log(self, limit=None):
        """Read log entries into a list of dictionaries."""
        self.ui.pushbuffer()
        commands.log(self.ui, self.repo, limit=limit, date=None, rev=None, user=None)
        res = self.ui.popbuffer().strip()

        logList = []
        for logentry in res.split("\n\n"):
            log = {}
            logList.append(log)
            for line in logentry.split("\n"):
                k, v = line.split(":", 1)
                assert k in ("changeset", "tag", "user", "date", "summary")
                log[k.strip()] = v.strip()
            log["parsed_date"] = util.parse_time_string(log["date"])
            local_id, unid = log["changeset"].split(":")
            log["local_id"] = int(local_id)
            log["unid"] = unid
        #        pprint(logList)
        return logList

def _get_log ( self , limit = None ) : self . ui . pushbuffer ( ) commands . log ( self . ui , self . repo , limit = limit , date = None , rev = None , user = None ) res = self . ui . popbuffer ( ) . strip ( ) logList = [ ] for logentry in res . split ( "\n\n" ) : log = { } logList . append ( log ) for line in logentry . split ( "\n" ) : k , v = line . split ( ":" , 1 ) assert k in ( "changeset" , "tag" , "user" , "date" , "summary" ) log [ k . strip ( ) ] = v . strip ( ) log [ "parsed_date" ] = util . parse_time_string ( log [ "date" ] ) local_id , unid = log [ "changeset" ] . split ( ":" ) log [ "local_id" ] = int ( local_id ) log [ "unid" ] = unid return logList

Read log entries into a list of dictionaries .


def _get_repo_info(self, environ, rev, reload=False):
        """Return a dictionary containing all files under source control.

        dirinfos:
            Dictionary containing direct members for every collection.
            {folderpath: (collectionlist, filelist), ...}
        files:
            Sorted list of all file paths in the manifest.
        filedict:
            Dictionary containing all files under source control.

        ::

            {'dirinfos': {'': (['wsgidav',
                                'tools',
                                'WsgiDAV.egg-info',
                                'tests'],
                               ['index.rst',
                                'wsgidav MAKE_DAILY_BUILD.launch',
                                'wsgidav run_server.py DEBUG.launch',
                                'wsgidav-paste.conf',
                                ...
                                'setup.py']),
                          'wsgidav': (['addons', 'samples', 'server', 'interfaces'],
                                      ['__init__.pyc',
                                       'dav_error.pyc',
                                       'dav_provider.pyc',
                                       ...
                                       'wsgidav_app.py']),
                           },
             'files': ['.hgignore',
                       'ADDONS.txt',
                       'wsgidav/samples/mysql_dav_provider.py',
                       ...
                       ],
             'filedict': {'.hgignore': True,
                           'README.txt': True,
                           'WsgiDAV.egg-info/PKG-INFO': True,
                           }
                           }
        """
        caches = environ.setdefault("wsgidav.hg.cache", {})
        if caches.get(compat.to_native(rev)) is not None:
            _logger.debug("_get_repo_info(%s): cache hit." % rev)
            return caches[compat.to_native(rev)]

        start_time = time.time()
        self.ui.pushbuffer()
        commands.manifest(self.ui, self.repo, rev)
        res = self.ui.popbuffer()
        files = []
        dirinfos = {}
        filedict = {}
        for file in res.split("\n"):
            if file.strip() == "":
                continue
            file = file.replace("\\", "/")
            # add all parent directories to 'dirinfos'
            parents = file.split("/")
            if len(parents) >= 1:
                p1 = ""
                for i in range(0, len(parents) - 1):
                    p2 = parents[i]
                    dir = dirinfos.setdefault(p1, ([], []))
                    if p2 not in dir[0]:
                        dir[0].append(p2)
                    if p1 == "":
                        p1 = p2
                    else:
                        p1 = "%s/%s" % (p1, p2)
                dirinfos.setdefault(p1, ([], []))[1].append(parents[-1])
            filedict[file] = True
        files.sort()

        cache = {"files": files, "dirinfos": dirinfos, "filedict": filedict}
        caches[compat.to_native(rev)] = cache
        _logger.info("_getRepoInfo(%s) took %.3f" % (rev, time.time() - start_time))
        return cache

def
_get_repo_info
(
self
,
environ
,
rev
,
reload
=
False
)
:
caches
=
environ
.
setdefault
(
"wsgidav.hg.cache"
,
{
}
)
if
caches
.
get
(
compat
.
to_native
(
rev
)
)
is
not
:
_logger
.
debug
(
"_get_repo_info(%s): cache hit."
%
rev
)
return
caches
[
compat
.
to_native
(
rev
)
]
start_time
=
time
.
time
(
)
self
.
ui
.
pushbuffer
(
)
commands
.
manifest
(
self
.
ui
,
self
.
repo
,
rev
)
res
=
self
.
ui
.
popbuffer
(
)
files
=
[
]
dirinfos
=
{
}
filedict
=
{
}
for
file
in
res
.
split
(
"\n"
)
:
if
file
.
strip
(
)
==
""
:
continue
file
=
file
.
replace
(
"\\"
,
"/"
)
parents
=
file
.
split
(
"/"
)
if
len
(
parents
)
>=
1
:
p1
=
""
for
i
in
range
(
0
,
len
(
parents
)
1
)
:
p2
=
parents
[
i
]
dir
=
dirinfos
.
setdefault
(
p1
,
(
[
]
,
[
]
)
)
if
p2
not
in
dir
[
0
]
:
dir
[
0
]
.
append
(
p2
)
if
p1
==
""
:
p1
=
p2
else
:
p1
=
"%s/%s"
%
(
p1
,
p2
)
dirinfos
.
setdefault
(
p1
,
(
[
]
,
[
]
)
)
[
1
]
.
append
(
parents
[
1
]
)
filedict
[
file
]
=
True
files
.
sort
(
)
cache
=
{
"files"
:
files
,
"dirinfos"
:
dirinfos
,
"filedict"
:
filedict
}
caches
[
compat
.
to_native
(
rev
)
]
=
cache
_logger
.
info
(
"_getRepoInfo(%s) took %.3f"
%
(
rev
,
time
.
time
(
)
start_time
)
)
return
cache

Return a dictionary containing all files under source control .


def get_resource_inst(self, path, environ):
        """Return HgResource object for path.

        See DAVProvider.get_resource_inst()
        """
        self._count_get_resource_inst += 1

        # HG expects the resource paths without leading '/'
        localHgPath = path.strip("/")
        rev = None
        cmd, rest = util.pop_path(path)

        if cmd == "":
            return VirtualCollection(
                path, environ, "root", ["edit", "released", "archive"]
            )
        elif cmd == "edit":
            localHgPath = rest.strip("/")
            rev = None
        elif cmd == "released":
            localHgPath = rest.strip("/")
            rev = "tip"
        elif cmd == "archive":
            if rest == "/":
                # Browse /archive: return a list of revision folders:
                loglist = self._get_log(limit=10)
                members = [compat.to_native(l["local_id"]) for l in loglist]
                return VirtualCollection(path, environ, "Revisions", members)
            revid, rest = util.pop_path(rest)
            try:
                int(revid)
            except Exception:
                # Tried to access /archive/anyname
                return None
            # Access /archive/19
            rev = revid
            localHgPath = rest.strip("/")
        else:
            return None

        # read mercurial repo into request cache
        cache = self._get_repo_info(environ, rev)

        if localHgPath in cache["filedict"]:
            # It is a version controlled file
            return HgResource(path, False, environ, rev, localHgPath)

        if localHgPath in cache["dirinfos"] or localHgPath == "":
            # It is an existing folder
            return HgResource(path, True, environ, rev, localHgPath)
        return None

def get_resource_inst ( self , path , environ ) : self . _count_get_resource_inst += 1 localHgPath = path . strip ( "/" ) rev = None cmd , rest = util . pop_path ( path ) if cmd == "" : return VirtualCollection ( path , environ , "root" , [ "edit" , "released" , "archive" ] ) elif cmd == "edit" : localHgPath = rest . strip ( "/" ) rev = None elif cmd == "released" : localHgPath = rest . strip ( "/" ) rev = "tip" elif cmd == "archive" : if rest == "/" : loglist = self . _get_log ( limit = 10 ) members = [ compat . to_native ( l [ "local_id" ] ) for l in loglist ] return VirtualCollection ( path , environ , "Revisions" , members ) revid , rest = util . pop_path ( rest ) try : int ( revid ) except Exception : return None rev = revid localHgPath = rest . strip ( "/" ) else : return None cache = self . _get_repo_info ( environ , rev ) if localHgPath in cache [ "filedict" ] : return HgResource ( path , False , environ , rev , localHgPath ) if localHgPath in cache [ "dirinfos" ] or localHgPath == "" : return HgResource ( path , True , environ , rev , localHgPath ) return None


def get_preferred_path(self):
        """Return preferred mapping for a resource mapping.

        Different URLs may map to the same resource, e.g.:
            '/a/b' == '/A/b' == '/a/b/'
        get_preferred_path() returns the same value for all these variants, e.g.:
            '/a/b/'   (assuming resource names considered case insensitive)

        @param path: a UTF-8 encoded, unquoted byte string.
        @return: a UTF-8 encoded, unquoted byte string.
        """
        if self.path in ("", "/"):
            return "/"
        # Append '/' for collections
        if self.is_collection and not self.path.endswith("/"):
            return self.path + "/"
        # TODO: handle case-sensitivity, depending on OS
        # (FileSystemProvider could do this with os.path:
        # (?) on unix we can assume that the path already matches exactly the case of filepath
        # on windows we could use path.lower() or get the real case from the
        # file system
        return self.path

def get_preferred_path ( self ) : if self . path in ( "" , "/" ) : return "/" if self . is_collection and not self . path . endswith ( "/" ) : return self . path + "/" return self . path

Return preferred mapping for a resource mapping .


def get_href(self):
        """Convert path to a URL that can be passed to XML responses.

        Byte string, UTF-8 encoded, quoted.

        See http://www.webdav.org/specs/rfc4918.html#rfc.section.8.3
        We are using the path-absolute option. i.e. starting with '/'.
        URI ; See section 3.2.1 of [RFC2068]
        """
        # Nautilus chokes, if href encodes '(' as '%28'
        # So we don't encode 'extra' and 'safe' characters (see rfc2068 3.2.1)
        safe = "/" + "!*'()," + "$-_|."
        return compat.quote(
            self.provider.mount_path
            + self.provider.share_path
            + self.get_preferred_path(),
            safe=safe,
        )

def get_href ( self ) : safe = "/" + "!*'()," + "$-_|." return compat . quote ( self . provider . mount_path + self . provider . share_path + self . get_preferred_path ( ) , safe = safe , )

Convert path to a URL that can be passed to XML responses .


def set_property_value(self, name, value, dry_run=False):
        """Set a property value or remove a property.

        value == None means 'remove property'.
        Raise HTTP_FORBIDDEN if property is read-only, or not supported.

        When dry_run is True, this function should raise errors, as in a real
        run, but MUST NOT change any data.

        This default implementation

        - raises HTTP_FORBIDDEN, if trying to modify a locking property
        - raises HTTP_FORBIDDEN, if trying to modify an immutable {DAV:}
          property
        - handles Windows' Win32LastModifiedTime to set the getlastmodified
          property, if enabled
        - stores everything else as dead property, if a property manager is
          present.
        - raises HTTP_FORBIDDEN, else

        Removing a non-existing prop is NOT an error.

        Note: RFC 4918 states that {DAV:}displayname 'SHOULD NOT be protected'

        A resource provider may override this method, to update supported custom
        live properties.
        """
        assert value is None or xml_tools.is_etree_element(value)

        if name in _lockPropertyNames:
            # Locking properties are always read-only
            raise DAVError(
                HTTP_FORBIDDEN, err_condition=PRECONDITION_CODE_ProtectedProperty
            )

        # Live property
        config = self.environ["wsgidav.config"]
        # hotfixes = config.get("hotfixes", {})
        mutableLiveProps = config.get("mutable_live_props", [])
        # Accept custom live property updates on resources if configured.
        if (
            name.startswith("{DAV:}")
            and name in _standardLivePropNames
            and name in mutableLiveProps
        ):
            # Please note that some properties should not be mutable according
            # to RFC4918. This includes the 'getlastmodified' property, which
            # it may still make sense to make mutable in order to support time
            # stamp changes from e.g. utime calls or the touch or rsync -a
            # commands.
            if name in ("{DAV:}getlastmodified", "{DAV:}last_modified"):
                try:
                    return self.set_last_modified(self.path, value.text, dry_run)
                except Exception:
                    _logger.warning(
                        "Provider does not support set_last_modified on {}.".format(
                            self.path
                        )
                    )

            # Unsupported or not allowed
            raise DAVError(HTTP_FORBIDDEN)

        # Handle MS Windows Win32LastModifiedTime, if enabled.
        # Note that the WebDAV client in Win7 and earler has issues and can't be used
        # with this so we ignore older clients. Others pre-Win10 should be tested.
        if name.startswith("{urn:schemas-microsoft-com:}"):
            agent = self.environ.get("HTTP_USER_AGENT", "None")
            win32_emu = config.get("hotfixes", {}).get("emulate_win32_lastmod", False)
            if win32_emu and "MiniRedir/6.1" not in agent:
                if "Win32LastModifiedTime" in name:
                    return self.set_last_modified(self.path, value.text, dry_run)
                elif "Win32FileAttributes" in name:
                    return True
                elif "Win32CreationTime" in name:
                    return True
                elif "Win32LastAccessTime" in name:
                    return True

        # Dead property
        pm = self.provider.prop_manager
        if pm and not name.startswith("{DAV:}"):
            refUrl = self.get_ref_url()
            if value is None:
                return pm.remove_property(refUrl, name, dry_run, self.environ)
            else:
                value = etree.tostring(value)
                return pm.write_property(refUrl, name, value, dry_run, self.environ)

        raise DAVError(HTTP_FORBIDDEN)

Set a property value or remove a property .


def remove_all_properties(self, recursive):
        """Remove all associated dead properties."""
        if self.provider.prop_manager:
            self.provider.prop_manager.remove_properties(
                self.get_ref_url(), self.environ
            )

def remove_all_properties ( self , recursive ) : if self . provider . prop_manager : self . provider . prop_manager . remove_properties ( self . get_ref_url ( ) , self . environ )


def is_locked(self):
        """Return True, if URI is locked."""
        if self.provider.lock_manager is None:
            return False
        return self.provider.lock_manager.is_url_locked(self.get_ref_url())

def is_locked ( self ) : if self . provider . lock_manager is None : return False return self . provider . lock_manager . is_url_locked ( self . get_ref_url ( ) )


def set_share_path(self, share_path):
        """Set application location for this resource provider.

        @param share_path: a UTF-8 encoded, unquoted byte string.
        """
        # if isinstance(share_path, unicode):
        #     share_path = share_path.encode("utf8")
        assert share_path == "" or share_path.startswith("/")
        if share_path == "/":
            share_path = ""  # This allows to code 'absPath = share_path + path'
        assert share_path in ("", "/") or not share_path.endswith("/")
        self.share_path = share_path

def set_share_path ( self , share_path ) : assert share_path == "" or share_path . startswith ( "/" ) if share_path == "/" : share_path = "" assert share_path in ( "" , "/" ) or not share_path . endswith ( "/" ) self . share_path = share_path

Set application location for this resource provider .


def ref_url_to_path(self, ref_url):
        """Convert a refUrl to a path, by stripping the share prefix.

        Used to calculate the <path> from a storage key by inverting get_ref_url().
        """
        return "/" + compat.unquote(util.lstripstr(ref_url, self.share_path)).lstrip(
            "/"
        )

def ref_url_to_path ( self , ref_url ) : return "/" + compat . unquote ( util . lstripstr ( ref_url , self . share_path ) ) . lstrip ( "/" )

Convert a refUrl to a path by stripping the share prefix .