diff options
| author | Carlos Konstanski <ckonstanski@pippiandcarlos.com> | 2017-10-26 15:16:59 -0600 |
|---|---|---|
| committer | Carlos Konstanski <ckonstanski@pippiandcarlos.com> | 2017-10-26 15:16:59 -0600 |
| commit | 4a1c4e8c3e6ca4ffbcd9ef38f07a1527226d7430 (patch) | |
| tree | 2ce07ca014433fd0349514b065595db48be94e8d | |
initial commit
| -rw-r--r-- | .gitignore | 2 | ||||
| -rwxr-xr-x | ci-script.py | 114 | ||||
| -rw-r--r-- | libs/.gitignore | 1 | ||||
| -rw-r--r-- | libs/__init__.py | 1 | ||||
| -rw-r--r-- | libs/environment.py | 53 | ||||
| -rw-r--r-- | libs/glance.py | 38 | ||||
| -rw-r--r-- | libs/keystone.py | 39 |
7 files changed, 248 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80fb99f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +*swp diff --git a/ci-script.py b/ci-script.py new file mode 100755 index 0000000..0e8ef6a --- /dev/null +++ b/ci-script.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +from __future__ import print_function +from libs.environment import * +from libs.glance import * +from libs.keystone import * +import argparse +import glob +import os +import shlex +import subprocess +import sys +import yaml + + +def run_shell_command(cmd): + return subprocess.check_output(cmd, shell=True) + + +def read_manifests(): + manifests = {} + manifests_glob = "%s/manifests/*.yaml" % os.path.dirname(os.path.realpath(__file__)) + for manifest_file in glob.glob(manifests_glob): + with open(manifest_file, "r") as myfile: + manifest = yaml.load(myfile.read()) + if manifest["image_name"] in manifests: + raise Exception("Duplicate image name in manifests: %s" % manifest["image_name"]) + manifests[manifest["image_name"]] = manifest + return manifests + + +def check_image_tag_current(glance, manifest): + image = glance.find("%s.%s" % (manifest["image_name"], manifest["image_type"])) + return image and str(manifest["tag"]) in image.tags + + +def create_image(manifest): + cmd = shlex.quote("disk-image-create %s -t %s -o /tmp/%s.%s 2>&1" % (manifest["elements"], manifest["image_type"], manifest["image_name"], manifest["image_type"])) + cmdparts = shlex.split(cmd) + env = manifest["extra_envs"] if "extra_envs" in manifest else None + p = subprocess.Popen(cmdparts, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) + out, err = p.communicate() + if p.returncode != 0: + raise Exception("Error running diskimage-builder:\n%s\n%s" % (out, err)) + + +def main(args): + env = Environment(args.os_auth_url, + args.os_project_name, + args.os_region, + args.os_username, + args.os_password, + args.os_project_domain_name, + args.os_user_domain_name, + args.os_identity_api_version, + args.os_image_api_version, + args.elements_path) + manifests = read_manifests() + keystone = Keystone(env) + glance = Glance(keystone, endpoint_type="publicURL") + for image_name, manifest in manifests.items(): + if not check_image_tag_current(glance, manifest): + create_image(manifest) + glance.upload("%s.%s" % (image_name, image_type), + manifest["image_type"], + "/tmp/%s.%s" % (manifest["image_name"], manifest["image_type"]), + manifest["tag"]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Image Foundry CI Process") + parser.add_argument("--os-auth-url", + required=False, + default=os.environ.get("OS_AUTH_URL", None), + help="OpenStack openrc environment variable: OS_AUTH_URL") + parser.add_argument("--os-project-name", + required=False, + default=os.environ.get("OS_PROJECT_NAME", None), + help="OpenStack openrc environment variable: OS_PROJECT_NAME") + parser.add_argument("--os-region", + required=False, + default=os.environ.get("OS_REGION", None), + help="OpenStack openrc environment variable: OS_REGION") + parser.add_argument("--os-username", + required=False, + default=os.environ.get("OS_USERNAME", None), + help="OpenStack openrc environment variable: OS_USERNAME") + parser.add_argument("--os-password", + required=False, + default=os.environ.get("OS_PASSWORD", None), + help="OpenStack openrc environment variable: OS_PASSWORD") + parser.add_argument("--os-project-domain-name", + required=False, + default=os.environ.get("OS_PROJECT_DOMAIN_NAME", None), + help="OpenStack openrc environment variable: OS_PROJECT_DOMAIN_NAME") + parser.add_argument("--os-user-domain-name", + required=False, + default=os.environ.get("OS_USER_DOMAIN_NAME", None), + help="OpenStack openrc environment variable: OS_USER_DOMAIN_NAME") + parser.add_argument("--os-identity-api-version", + required=False, + default=os.environ.get("OS_IDENTITY_API_VERSION", None), + help="OpenStack openrc environment variable: OS_IDENTITY_API_VERSION") + parser.add_argument("--os-image-api-version", + required=False, + default=os.environ.get("OS_IMAGE_API_VERSION", None), + help="OpenStack openrc environment variable: OS_IMAGE_API_VERSION") + parser.add_argument("--elements-path", + required=False, + default=os.environ.get("ELEMENTS_PATH", None), + help="diskimage-builder environment variable: ELEMENTS_PATH") + args = parser.parse_args() + main(args) + sys.exit(0) diff --git a/libs/.gitignore b/libs/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/libs/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/libs/__init__.py b/libs/__init__.py new file mode 100644 index 0000000..609997c --- /dev/null +++ b/libs/__init__.py @@ -0,0 +1 @@ +__all__ = ["environment", "keystone", "glance"] diff --git a/libs/environment.py b/libs/environment.py new file mode 100644 index 0000000..f78f3d1 --- /dev/null +++ b/libs/environment.py @@ -0,0 +1,53 @@ +import sys +import os + + +class Environment(object): + def __init__(self, + os_auth_url, + os_project_name, + os_region, + os_username, + os_password, + os_project_domain_name, + os_user_domain_name, + os_identity_api_version, + os_image_api_version, + elements_path, + extra_envs={}): + if os_auth_url is None or \ + os_project_name is None or \ + os_region is None or \ + os_username is None or \ + os_password is None or \ + os_project_domain_name is None or \ + os_user_domain_name is None or \ + os_identity_api_version is None or \ + os_image_api_version is None or \ + elements_path is None: + print("All required environment arguments must have a value. You passed in:" + \ + "\nos_auth_url = " + str(os_auth_url) + \ + "\nos_project_name = " + str(os_project_name) + \ + "\nos_region = " + str(os_region) + \ + "\nos_username = " + str(os_username) + \ + "\nos_password = " + str(os_password) + \ + "\nos_project_domain_name = " + str(os_project_domain_name) + \ + "\nos_user_domain_name = " + str(os_user_domain_name) + \ + "\nos_identity_api_version = " + str(os_identity_api_version) + \ + "\nos_image_api_version = " + str(os_image_api_version) + \ + "\nelements_path = " + str(elements_path)) + sys.exit(1) + else: + self.os_auth_url = os_auth_url + self.os_project_name = os_project_name + self.os_region = os_region + self.os_username = os_username + self.os_password = os_password + self.os_project_domain_name = os_project_domain_name + self.os_user_domain_name = os_user_domain_name + self.os_identity_api_version = os_identity_api_version + self.os_image_api_version = os_image_api_version + self.elements_path = elements_path + self.extra_envs = {} + for key, value in [(k, v) for (k, v) in extra_envs.items() if v]: + self.extra_envs[key] = value diff --git a/libs/glance.py b/libs/glance.py new file mode 100644 index 0000000..b2a5c8d --- /dev/null +++ b/libs/glance.py @@ -0,0 +1,38 @@ +from __future__ import print_function +import datetime +import glanceclient +import json +import time +import traceback + + +class Glance(object): + def __init__(self, keystone, endpoint_type="publicURL"): + self.keystone = keystone + self.glance = glanceclient.Client(keystone.os_image_api_version, + endpoint=keystone.get_glance_url(endpoint_type=endpoint_type), + session=keystone.session) + + def find(self, image_name): + for image in self.glance.images.list(): + if image.name == image_name: + return image + return None + + def delete(self, image_name): + image = self.find(image_name) + if image: + self.glance.images.delete(image.id) + + def upload(self, image_name, image_type, file_path, tag): + self.delete(image_name) + image = self.glance.images.create(name=image_name) + if image.status != "queued": + raise Exception("Image did not enter queued state upon create.") + image = self.glance.images.update(image.id, disk_format=image_type) + image = self.glance.images.update(image.id, container_format="bare") + image = self.glance.images.update(image.id, visibility="private") + image = self.glance.images.update(image.id, tags=[str(tag)]) + with open(file_path, "rb") as myfile: + self.glance.images.upload(image.id, myfile) + return image.id diff --git a/libs/keystone.py b/libs/keystone.py new file mode 100644 index 0000000..05c4d8c --- /dev/null +++ b/libs/keystone.py @@ -0,0 +1,39 @@ +from keystoneauth1.identity import v3 +from keystoneauth1 import session +from keystoneclient.v3 import client as keystoneclient + + +class Project(object): + def __init__(self, name, uuid): + self.name = name + self.uuid = uuid + + +class Keystone(object): + def __init__(self, env): + if env.os_identity_api_version != "3": + raise Exception("Keystone v3 is required. You are using version %s" % env.os_identity_api_version) + self.os_identity_api_version = env.os_identity_api_version + self.os_image_api_version = env.os_image_api_version + self.os_auth_url = env.os_auth_url.replace("v2.0", "v3") + + auth = v3.Password(username=env.os_username, + password=env.os_password, + project_domain_name=env.os_project_domain_name, + user_domain_name=env.os_user_domain_name, + project_name=env.os_project_name, + auth_url=self.os_auth_url) + self.session = session.Session(auth=auth) + self.client = keystoneclient.Client(session=self.session, + region_name=env.os_region) + + def get_projects(self): + return map(lambda x: Project(x.name, x.id), + [p for p in self.client.projects.list() if p.enabled]) + + def get_catalog_url(self, service_type=None, endpoint_type="publicURL"): + return self.session.get_endpoint(service_type=service_type, + endpoint_type=endpoint_type) + + def get_glance_url(self, endpoint_type="publicURL"): + return self.get_catalog_url(service_type="image", endpoint_type=endpoint_type) |
