Example #1
0
File: v1.py Project: sitedata/lorax
def v1_upload_log(upload_uuid):
    """Returns an upload's log

    **GET /api/v1/upload/log/<upload_uuid>**

      Example response::

          {
            "status": true,
            "upload_id": "b637c411-9d9d-4279-b067-6c8d38e3b211",
            "log": "< PLAY [localhost] >..."
          }
    """
    if VALID_API_STRING.match(upload_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    try:
        upload = get_upload(api.config["COMPOSER_CFG"]["upload"], upload_uuid)
    except RuntimeError as error:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(error)
                       }])
    return jsonify(status=True, upload_id=upload_uuid, log=upload.upload_log)
Example #2
0
File: v1.py Project: sitedata/lorax
def v1_compose_uploads_delete(upload_uuid):
    """Delete an upload and disassociate it from its compose

    **DELETE /api/v1/upload/delete/<upload_uuid>**

      Example response::

          {
            "status": true,
            "upload_id": "572eb0d0-5348-4600-9666-14526ba628bb"
          }
    """
    if VALID_API_STRING.match(upload_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    try:
        uuid_remove_upload(api.config["COMPOSER_CFG"], upload_uuid)
        delete_upload(api.config["COMPOSER_CFG"]["upload"], upload_uuid)
    except RuntimeError as error:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(error)
                       }])
    return jsonify(status=True, upload_id=upload_uuid)
Example #3
0
File: v1.py Project: sitedata/lorax
def v1_upload_cancel(upload_uuid):
    """Cancel an upload that is either queued or in progress

    **DELETE /api/v1/upload/cancel/<upload_uuid>**

      Example response::

          {
            "status": true,
            "upload_id": "037a3d56-b421-43e9-9935-c98350c89996"
          }
    """
    if VALID_API_STRING.match(upload_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    try:
        cancel_upload(api.config["COMPOSER_CFG"]["upload"], upload_uuid)
    except RuntimeError as error:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(error)
                       }])
    return jsonify(status=True, upload_id=upload_uuid)
Example #4
0
File: v1.py Project: sitedata/lorax
def v1_providers_delete(provider_name, profile):
    """Delete a provider's profile settings

    **DELETE /api/v1/upload/providers/delete/<provider_name>/<profile>**

      Example response::

          {
            "status": true
          }
    """
    if None in (VALID_API_STRING.match(provider_name),
                VALID_API_STRING.match(profile)):
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    try:
        delete_profile(api.config["COMPOSER_CFG"]["upload"], provider_name,
                       profile)
    except Exception as e:
        error = {"id": UPLOAD_ERROR, "msg": str(e)}
        return jsonify(status=False, errors=[error])
    return jsonify(status=True)
Example #5
0
File: v1.py Project: sitedata/lorax
def v1_upload_reset(upload_uuid):
    """Reset an upload so it can be attempted again

    **POST /api/v1/upload/reset/<upload_uuid>**

      Optionally pass in a new image name and/or new settings.

      Example request::

          {
            "image_name": "My renamed image",
            "settings": {
              "resource_group": "ROLL",
              "storage_account_name": "ME",
              "storage_container": "I",
              "location": "AIN'T",
              "subscription_id": "THE",
              "client_id": "SHARPEST",
              "secret": "TOOL",
              "tenant": "IN"
            }
          }

      Example response::

          {
            "status": true,
            "upload_id": "c75d5d62-9d26-42fc-a8ef-18bb14679fc7"
          }
    """
    if VALID_API_STRING.match(upload_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    parsed = request.get_json(cache=False)
    image_name = parsed.get("image_name") if parsed else None
    settings = parsed.get("settings") if parsed else None

    try:
        reset_upload(api.config["COMPOSER_CFG"]["upload"], upload_uuid,
                     image_name, settings)
    except RuntimeError as error:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(error)
                       }])
    return jsonify(status=True, upload_id=upload_uuid)
Example #6
0
File: v1.py Project: sitedata/lorax
def v1_upload_info(upload_uuid):
    """Returns information about a given upload

    **GET /api/v1/upload/info/<upload_uuid>**

      Example response::

          {
            "status": true,
            "upload": {
              "creation_time": 1565620940.069004,
              "image_name": "My Image",
              "image_path": "/var/lib/lorax/composer/results/b6218e8f-0fa2-48ec-9394-f5c2918544c4/disk.vhd",
              "provider_name": "azure",
              "settings": {
                "resource_group": "SOMEBODY",
                "storage_account_name": "ONCE",
                "storage_container": "TOLD",
                "location": "ME",
                "subscription_id": "THE",
                "client_id": "WORLD",
                "secret": "IS",
                "tenant": "GONNA"
              },
              "status": "FAILED",
              "uuid": "b637c411-9d9d-4279-b067-6c8d38e3b211"
            }
          }
    """
    if VALID_API_STRING.match(upload_uuid) is None:
        return jsonify(status=False,
                       errors=[{
                           "id": INVALID_CHARS,
                           "msg": "Invalid characters in API path"
                       }]), 400

    try:
        upload = get_upload(api.config["COMPOSER_CFG"]["upload"],
                            upload_uuid).summary()
    except RuntimeError as error:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(error)
                       }])
    return jsonify(status=True, upload=upload)
Example #7
0
File: v1.py Project: dwlehman/lorax
def v1_projects_source_info(source_ids):
    """Return detailed info about the list of sources

    **/api/v1/projects/source/info/<source-ids>**

      Return information about the comma-separated list of source ids. Or all of the
      sources if '*' is passed. Note that general globbing is not supported, only '*'.

      Immutable system sources will have the "system" field set to true. User added sources
      will have it set to false. System sources cannot be changed or deleted.

      Example::

          {
            "errors": [],
            "sources": {
              "fedora": {
                "check_gpg": true,
                "check_ssl": true,
                "gpgkey_urls": [
                  "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64"
                ],
                "id": "fedora",
                "name": "Fedora $releasever - $basearch",
                "proxy": "http://proxy.brianlane.com:8123",
                "system": true,
                "type": "yum-metalink",
                "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64"
              }
            }
          }

    In v0 the ``name`` field was used for the id (a short name for the repo). In v1 ``name`` changed
    to ``id`` and ``name`` is now used for the longer descriptive name of the repository.
    """
    if VALID_API_STRING.match(source_ids) is None:
        return jsonify(status=False,
                       errors=[{
                           "id": INVALID_CHARS,
                           "msg": "Invalid characters in API path"
                       }]), 400

    out_fmt = request.args.get("format", "json")
    if VALID_API_STRING.match(out_fmt) is None:
        return jsonify(status=False,
                       errors=[{
                           "id": INVALID_CHARS,
                           "msg": "Invalid characters in format argument"
                       }]), 400

    # Return info on all of the sources
    if source_ids == "*":
        with api.config["DNFLOCK"].lock:
            source_ids = ",".join(
                r.id for r in api.config["DNFLOCK"].dbo.repos.iter_enabled())

    sources = {}
    errors = []
    system_sources = get_repo_sources("/etc/yum.repos.d/*.repo")
    for source in source_ids.split(","):
        with api.config["DNFLOCK"].lock:
            repo = api.config["DNFLOCK"].dbo.repos.get(source, None)
        if not repo:
            errors.append({
                "id": UNKNOWN_SOURCE,
                "msg": "%s is not a valid source" % source
            })
            continue
        sources[repo.id] = repo_to_source(repo,
                                          repo.id in system_sources,
                                          api=1)

    if out_fmt == "toml" and not errors:
        # With TOML output we just want to dump the raw sources, skipping the errors
        return toml.dumps(sources)
    elif out_fmt == "toml" and errors:
        # TOML requested, but there was an error
        return jsonify(status=False, errors=errors), 400
    else:
        return jsonify(sources=sources, errors=errors)
Example #8
0
File: v1.py Project: sitedata/lorax
def v1_compose_uploads_schedule(compose_uuid):
    """Schedule an upload of a compose to a given cloud provider

    **POST /api/v1/uploads/schedule/<compose_uuid>**

      The body can specify either a pre-existing profile to use (as returned by
      `/uploads/providers`) or one-time use settings for the provider.

      Example with upload profile::

          {
            "image_name": "My Image",
            "provider": "azure",
            "profile": "production-azure-settings"
          }

      Example with upload settings::

          {
            "image_name": "My Image",
            "provider": "azure",
            "settings": {
              "resource_group": "SOMEBODY",
              "storage_account_name": "ONCE",
              "storage_container": "TOLD",
              "location": "ME",
              "subscription_id": "THE",
              "client_id": "WORLD",
              "secret": "IS",
              "tenant": "GONNA"
            }
          }

      Example response::

          {
            "status": true,
            "upload_id": "572eb0d0-5348-4600-9666-14526ba628bb"
          }
    """
    if VALID_API_STRING.match(compose_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    parsed = request.get_json(cache=False)
    if not parsed:
        return jsonify(status=False,
                       errors=[{
                           "id": MISSING_POST,
                           "msg": "Missing POST body"
                       }]), 400

    try:
        image_name = parsed["image_name"]
        provider_name = parsed["provider"]
        if "profile" in parsed:
            # Load a specific profile for this provider
            profile = parsed["profile"]
            settings = load_settings(api.config["COMPOSER_CFG"]["upload"],
                                     provider_name, profile)
        else:
            settings = parsed["settings"]
    except KeyError as e:
        error = {"id": UPLOAD_ERROR, "msg": f'Missing parameter {str(e)}!'}
        return jsonify(status=False, errors=[error]), 400
    try:
        compose_type = uuid_status(api.config["COMPOSER_CFG"],
                                   compose_uuid)["compose_type"]
        provider = resolve_provider(api.config["COMPOSER_CFG"]["upload"],
                                    provider_name)
        if "supported_types" in provider and compose_type not in provider[
                "supported_types"]:
            raise RuntimeError(
                f'Type "{compose_type}" is not supported by provider "{provider_name}"!'
            )
    except Exception as e:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(e)
                       }]), 400

    try:
        upload_id = uuid_schedule_upload(api.config["COMPOSER_CFG"],
                                         compose_uuid, provider_name,
                                         image_name, settings)
    except RuntimeError as e:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(e)
                       }]), 400
    return jsonify(status=True, upload_id=upload_id)
Example #9
0
File: v1.py Project: sitedata/lorax
def v1_compose_info(uuid):
    """Return detailed info about a compose

    **/api/v1/compose/info/<uuid>**

      Get detailed information about the compose. The returned JSON string will
      contain the following information:

        * id - The uuid of the comoposition
        * config - containing the configuration settings used to run Anaconda
        * blueprint - The depsolved blueprint used to generate the kickstart
        * commit - The (local) git commit hash for the blueprint used
        * deps - The NEVRA of all of the dependencies used in the composition
        * compose_type - The type of output generated (tar, iso, etc.)
        * queue_status - The final status of the composition (FINISHED or FAILED)

      Example::

          {
            "commit": "7078e521a54b12eae31c3fd028680da7a0815a4d",
            "compose_type": "tar",
            "config": {
              "anaconda_args": "",
              "armplatform": "",
              "compress_args": [],
              "compression": "xz",
              "image_name": "root.tar.xz",
              ...
            },
            "deps": {
              "packages": [
                {
                  "arch": "x86_64",
                  "epoch": "0",
                  "name": "acl",
                  "release": "14.el7",
                  "version": "2.2.51"
                }
              ]
            },
            "id": "c30b7d80-523b-4a23-ad52-61b799739ce8",
            "queue_status": "FINISHED",
            "blueprint": {
              "description": "An example kubernetes master",
              ...
            },
            "uploads": [
                {
                    "creation_time": 1568150660.524401,
                    "image_name": "glusterfs server",
                    "image_path": "/var/lib/lorax/composer/results/c30b7d80-523b-4a23-ad52-61b799739ce8/disk.vhd",
                    "provider_name": "azure",
                    "settings": {
                        "client_id": "need",
                        "location": "need",
                        "resource_group": "group",
                        "secret": "need",
                        "storage_account_name": "need",
                        "storage_container": "need",
                        "subscription_id": "need",
                        "tenant": "need"
                    },
                    "status": "FAILED",
                    "uuid": "21898dfd-9ac9-4e22-bb1d-7f12d0129e65"
                }
            ]
          }
    """
    if VALID_API_STRING.match(uuid) is None:
        return jsonify(status=False,
                       errors=[{
                           "id": INVALID_CHARS,
                           "msg": "Invalid characters in API path"
                       }]), 400

    try:
        info = uuid_info(api.config["COMPOSER_CFG"], uuid, api=1)
    except Exception as e:
        return jsonify(status=False,
                       errors=[{
                           "id": COMPOSE_ERROR,
                           "msg": str(e)
                       }]), 400

    if info is None:
        return jsonify(status=False,
                       errors=[{
                           "id": UNKNOWN_UUID,
                           "msg": "%s is not a valid build uuid" % uuid
                       }]), 400
    else:
        return jsonify(**info)
Example #10
0
File: v1.py Project: sitedata/lorax
def v1_compose_status(uuids):
    """Return the status of the listed uuids

    **/api/v1/compose/status/<uuids>[?blueprint=<blueprint_name>&status=<compose_status>&type=<compose_type>]**

      Return the details for each of the comma-separated list of uuids. A uuid of '*' will return
      details for all composes.

      Example::

          {
            "uuids": [
              {
                "id": "8c8435ef-d6bd-4c68-9bf1-a2ef832e6b1a",
                "blueprint": "http-server",
                "queue_status": "FINISHED",
                "job_created": 1517523644.2384307,
                "job_started": 1517523644.2551234,
                "job_finished": 1517523689.9864314,
                "version": "0.0.2"
              },
              {
                "id": "45502a6d-06e8-48a5-a215-2b4174b3614b",
                "blueprint": "glusterfs",
                "queue_status": "FINISHED",
                "job_created": 1517363442.188399,
                "job_started": 1517363442.325324,
                "job_finished": 1517363451.653621,
                "version": "0.0.6",
                "uploads": [
                    {
                        "creation_time": 1568150660.524401,
                        "image_name": "glusterfs server",
                        "image_path": null,
                        "provider_name": "azure",
                        "settings": {
                            "client_id": "need",
                            "location": "need",
                            "resource_group": "group",
                            "secret": "need",
                            "storage_account_name": "need",
                            "storage_container": "need",
                            "subscription_id": "need",
                            "tenant": "need"
                        },
                        "status": "WAITING",
                        "uuid": "21898dfd-9ac9-4e22-bb1d-7f12d0129e65"
                    }
                ]
              }
            ]
          }
    """
    if VALID_API_STRING.match(uuids) is None:
        return jsonify(status=False,
                       errors=[{
                           "id": INVALID_CHARS,
                           "msg": "Invalid characters in API path"
                       }]), 400

    blueprint = request.args.get("blueprint", None)
    status = request.args.get("status", None)
    compose_type = request.args.get("type", None)

    results = []
    errors = []

    if uuids.strip() == '*':
        queue_status_dict = queue_status(api.config["COMPOSER_CFG"], api=1)
        queue_new = queue_status_dict["new"]
        queue_running = queue_status_dict["run"]
        candidates = queue_new + queue_running + build_status(
            api.config["COMPOSER_CFG"], api=1)
    else:
        candidates = []
        for uuid in [n.strip().lower() for n in uuids.split(",")]:
            details = uuid_status(api.config["COMPOSER_CFG"], uuid, api=1)
            if details is None:
                errors.append({
                    "id": UNKNOWN_UUID,
                    "msg": "%s is not a valid build uuid" % uuid
                })
            else:
                candidates.append(details)

    for details in candidates:
        if blueprint is not None and details['blueprint'] != blueprint:
            continue

        if status is not None and details['queue_status'] != status:
            continue

        if compose_type is not None and details['compose_type'] != compose_type:
            continue

        results.append(details)

    return jsonify(uuids=results, errors=errors)