{"id":20066280,"url":"https://github.com/ossobv/keystone-light","last_synced_at":"2025-03-02T11:15:00.239Z","repository":{"id":57438377,"uuid":"228368258","full_name":"ossobv/keystone-light","owner":"ossobv","description":"A (low-dependency) limited OpenStack Identity API v3 python client","archived":false,"fork":false,"pushed_at":"2024-01-30T21:11:57.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-13T10:48:44.583Z","etag":null,"topics":["openstack-keystone","openstack-swift","python-library"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ossobv.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-16T11:05:47.000Z","updated_at":"2024-01-30T21:12:01.000Z","dependencies_parsed_at":"2022-08-29T08:40:31.214Z","dependency_job_id":null,"html_url":"https://github.com/ossobv/keystone-light","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ossobv%2Fkeystone-light","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ossobv%2Fkeystone-light/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ossobv%2Fkeystone-light/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ossobv%2Fkeystone-light/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ossobv","download_url":"https://codeload.github.com/ossobv/keystone-light/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241494184,"owners_count":19971871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openstack-keystone","openstack-swift","python-library"],"created_at":"2024-11-13T13:55:46.654Z","updated_at":"2025-03-02T11:15:00.220Z","avatar_url":"https://github.com/ossobv.png","language":"Python","readme":"keystone-light :: A limited Identity API v3 python client\n=========================================================\n\nkeystone-light implements a Python interface to a very limited subset of\nthe `OpenStack Identity API v3`_.\n\nInitial goal: *access to OpenStack Swift, using the Identity API v3, but\nwith a lot fewer dependencies.*\n\nAs of this writing, the ``python-keystoneclient`` requires\n``keystoneauth1`` and ``oslo.*``, which in turn require some more. We\nonly require the *ubiquitous* ``requests`` (and ``PyYAML``), which you\ngenerally already have installed anyway.\n\n\nExample usage\n-------------\n\n.. code-block:: python\n\n    #!/usr/bin/env python3\n    from urllib.parse import urljoin\n\n    import requests\n    from keystone_light import Cloud, CloudsYamlConfig, PermissionDenied\n\n\n    def get_projects(cloud):\n        \"Yields projects, sorted by domain and project name\"\n        domains = cloud.get_domains()\n        for domain in sorted(domains, key=(lambda x: x.name)):\n            if domain.name == 'Default':\n                # print('WARN: skipping domain Default (fixme?)')\n                continue\n\n            projects = domain.get_projects()\n            for project in sorted(projects, key=(lambda x: x.name)):\n                project.domain = domain\n                yield project\n\n    def _give_us_project_perms_through_admin_group(project):\n        \"\"\"\n        Make sure we are in the *-admin group. Make sure the *-admin\n        group has permissions on the project.\n        \"\"\"\n        cloud = project.cloud\n        dom_admin_group = project.domain.get_admin_group()\n\n        # First check if we're member of the group at all.\n        token = cloud.get_system_token()\n        auth_headers = {'X-Auth-Token': str(token)}\n        try:\n            # FIXME: Undocumented access to system_token!\n            user_id = token.data['user']['id']\n            assert user_id and isinstance(user_id, str), user_id\n        except KeyError:\n            raise ValueError('missing user.id?', token.data)\n\n        # Are we in the *-admin group?\n        url = urljoin(\n            cloud.base_url,\n            '/v3/groups/{group_id}/users/{user_id}'.format(\n                group_id=dom_admin_group.id, user_id=user_id))\n        out = requests.head(url, headers=auth_headers)\n        if out.status_code == 404:\n            # Add us to the group.\n            out = requests.put(url, headers=auth_headers)\n            assert out.status_code == 204, (\n                'PUT', url, out.status_code, out.text)\n            # Double check.\n            out = requests.head(url, headers=auth_headers)\n        assert out.status_code == 204, (\n            'HEAD', url, out.status_code, out.text)\n\n        # Grant *-admin power to the project.\n        admin_role = cloud.get_role(name='admin')  # or 'reader'\n        url = urljoin(\n            cloud.base_url,\n            '/v3/projects/{project_id}/groups/{group_id}/roles/{role_id}'.format(\n                project_id=project.id, group_id=dom_admin_group.id,\n                role_id=admin_role.id))\n        out = requests.put(url, headers=auth_headers)\n        assert out.status_code in (201, 204), (\n            'PUT', url, out.status_code, out.text)\n\n    def get_swift_stat_ensuring_permissions(project):\n        \"Get Swift v1 stat on a project (previously: tenant)\"\n        try:\n            stat = project.get_swift().get_stat()\n        except PermissionDenied:\n            # We don't have permission to access the project? Upgrade the\n            # permissions and try again.\n            _give_us_project_perms_through_admin_group(project)\n        else:\n            return stat\n\n        # Try again. Should succeed now, with the added permissions.\n        try:\n            stat = project.get_swift().get_stat()\n        except PermissionDenied as e:\n            raise MyPermissionDenied(\n                'EPERM on {domain}.{project}: {exc} {exc_args}'.format(\n                    domain=project.domain.name, project=project.name,\n                    exc=e.__class__.__name__, exc_args=e.args)) from e\n        else:\n            return stat\n\n\n    # Take config from ~/.config/openstack/clouds.yaml and select\n    # 'my-cloud-admin', like the openstack(1) --os-cloud option.\n    config = CloudsYamlConfig('my-cloud-admin')\n    cloud = Cloud(config)\n    for project in get_projects(cloud):\n        swift_stat = get_swift_stat_ensuring_permissions(project)\n        print('{:15s} {:23s} {:21d} B ({} objects, {} containers)'.format(\n            project.domain.name[0:15], project.name,\n            int(swift_stat['X-Account-Bytes-Used']),\n            swift_stat['X-Account-Object-Count'],\n            swift_stat['X-Account-Container-Count']))\n\n\nExample output\n--------------\n\n.. code-block:: console\n\n    $ python3 example.py\n    domainx         project                  3489 B (2 objects, 1 containers)\n    domainx         otherproject       1455042022 B (267 objects, 1 containers)\n    ...\n\n\nSwift Example usage\n-------------------\n\n.. code-block:: python\n\n    from keystone_light import Cloud, DirectConfig\n\n    KEYSTONE_URL = 'https://\u003cDOMAIN\u003e:\u003cUSER\u003e:\u003cPASS\u003e@KEYSTONE'\n    SWIFT_PROJECT = '\u003cDOMAIN\u003e:\u003cPROJECT\u003e'\n    SWIFT_CONTAINER = 'some-container'\n\n    config = DirectConfig(KEYSTONE_URI)\n    project = Cloud(config).get_current_project()\n    assert project.get_fullname() == SWIFT_PROJECT, project.get_fullname()\n\n    swift = project.get_swift()\n    container = swift.get_container(SWIFT_CONTAINER)\n\n    # (Re-)upload file:\n    filename = ('bloblet.bin' if False else 'blobzilla.bin')\n    with open(filename, 'rb') as fp:\n        try:\n            container.delete(filename)\n        except FileNotFoundError:\n            pass\n        # TIP: Use ChunkIteratorIOBaseWrapper(fp) if the input file\n        # is a pipe/stream.\n        container.put(filename, fp)\n\n    # Download file:\n    filename2 = '{}.retrieved'.format(filename)\n    with container.get(filename) as response, \\\n            open(filename2, 'wb') as fp:\n        for chunk in response.iter_content(chunk_size=8192):\n            fp.write(chunk)\n\n    # Check and compare:\n    with open(filename, 'rb') as fp, \\\n            open(filename2, 'rb') as fp2:\n        buf = buf2 = True\n        while buf and buf2:\n            buf = fp.read(8192)\n            buf2 = fp2.read(8192)\n            assert buf == buf2\n        assert buf == buf2\n\nAnd an example with timing:\n\n.. code-block:: python\n\n    from timeit import timeit\n\n    # ...\n\n    # Download file:\n    filename2 = '{}.retrieved'.format(filename)\n    def _get():\n        with container.get(filename) as response, \\\n                open(filename2, 'wb') as fp:\n            for chunk in response.iter_content(chunk_size=8192):\n                fp.write(chunk)\n    print('{:7.3f} GET'.format(timeit(number=1, stmt=_get)))\n\n\n.. _`OpenStack Identity API v3`: https://docs.openstack.org/api-ref/identity/v3/\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fossobv%2Fkeystone-light","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fossobv%2Fkeystone-light","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fossobv%2Fkeystone-light/lists"}