Parallelize builds
diff --git a/ci/build-project-matrix b/ci/build-project-matrix
new file mode 100755
index 0000000..29a7e8d
--- /dev/null
+++ b/ci/build-project-matrix
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import glob
+import sys
+import json
+
+
+json.dump(
+ sorted([r.split("/")[-1] for r in glob.glob("images/openstack/projects/*")]),
+ sys.stdout,
+ separators=(",", ":"),
+)
diff --git a/ci/build-release-matrix b/ci/build-release-matrix
new file mode 100755
index 0000000..25e689d
--- /dev/null
+++ b/ci/build-release-matrix
@@ -0,0 +1,10 @@
+#!/usr/bin/env python3
+
+import glob
+import sys
+import json
+
+
+json.dump([
+ r.split('/')[-1] for r in glob.glob('images/openstack/releases/*')
+], sys.stdout, separators=(',', ':'))
diff --git a/ci/openstack-image-build-env b/ci/openstack-image-build-env
new file mode 100755
index 0000000..ba00973
--- /dev/null
+++ b/ci/openstack-image-build-env
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+import sys
+
+PROFILES = {
+ "cinder": "ceph qemu",
+ "nova": "ceph openvswitch configdrive qemu migration",
+ "neutron": "openvswitch vpn",
+ "keystone": "apache ldap openidc",
+ "horizon": "apache",
+ "monasca-api": "apache influxdb",
+ "ironic": "ipxe ipmi qemu tftp",
+ "glance": "ceph",
+ "monasca-persister": "influxdb",
+ "placement": "apache",
+}
+DIST_PACKAGES = {
+ "heat": "curl",
+ "designate": "bind9utils",
+ "nova": "ovmf qemu-efi-aarch64",
+ "neutron": "jq ethtool lshw",
+ "monasca-agent": "iproute2 libvirt-clients lshw",
+ "ironic": "ethtool lshw iproute2",
+}
+PIP_PACKAGES = {
+ "neutron": "neutron-vpnaas",
+ "monasca-agent": "libvirt-python python-glanceclient python-neutronclient python-novaclient py3nvml",
+ "horizon": "designate-dashboard heat-dashboard ironic-ui magnum-ui neutron-vpnaas-dashboard octavia-dashboard senlin-dashboard monasca-ui",
+ "ironic": "python-dracclient sushy",
+ "placement": "httplib2",
+}
+PLATFORMS = {
+ "nova": "linux/amd64,linux/arm64",
+ "neutron": "linux/amd64,linux/arm64",
+}
+
+project = sys.argv[1]
+release = sys.argv[2]
+
+ref = open(f"images/openstack/projects/{project}/{release}/ref", "r").read().strip()
+profiles = PROFILES.get(project, "")
+dist_packages = DIST_PACKAGES.get(project, "")
+pip_packages = PIP_PACKAGES.get(project, "")
+platforms = PLATFORMS.get(project, "")
+
+print(f"PROJECT_REF={ref}")
+print(f"PROFILES={profiles}")
+print(f"DIST_PACKAGES={dist_packages}")
+print(f"PIP_PACKAGES={pip_packages}")
+print(f"PLATFORMS={platforms}")