Merge pull request #1 from apache/multiple-environments
Make it possible to discover instances from various IBM cloud accounts
diff --git a/README.md b/README.md
index df88f98..421bbc1 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,14 @@
Setup
---
- $ virtualenv venv
+ $ python3 -m venv venv
$ source venv/bin/activate
$ pip install -r requirements.txt
+On BigSur Mac may have to do:
+
+ $ env LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" CFLAGS="-I$(brew --prefix openssl@1.1)/include" pip install -r requirements.txt
+
Provisioning VMs
---
diff --git a/production b/production
index da6f32d..896ab1c 100644
--- a/production
+++ b/production
@@ -141,8 +141,8 @@
ram: 8
couchdb-worker-x86-64-debian-dal-1-08:
instance:
- created_at: '2020-12-04T17:10:11Z'
- id: 0717_07f645ee-bc5b-4dfc-a342-b59b9fe9c3cd
+ created_at: '2021-03-08T21:13:51Z'
+ id: 0717_d015b177-e247-4c10-9fdd-a06009127409
name: couchdb-worker-x86-64-debian-dal-1-08
profile: cx2-4x8
subnet: couchdb-ci-farm-dal-1
@@ -150,7 +150,7 @@
zone: us-south-1
ip_addrs:
bastion: 169.48.153.153
- private: 10.240.0.7
+ private: 10.240.0.4
public: null
system:
arch: amd64
diff --git a/ssh.cfg b/ssh.cfg
index 699d104..3a596c5 100644
--- a/ssh.cfg
+++ b/ssh.cfg
@@ -71,7 +71,7 @@
ControlPersist 30m
Host couchdb-worker-x86-64-debian-dal-1-08
- Hostname 10.240.0.7
+ Hostname 10.240.0.4
User root
StrictHostKeyChecking no
ProxyCommand /usr/bin/ssh -F ./ssh.cfg -W %h:%p -q root@169.48.153.153
diff --git a/tools/gen-config b/tools/gen-config
index 4df5e2f..e1e0932 100755
--- a/tools/gen-config
+++ b/tools/gen-config
@@ -1,7 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import argparse as ap
-import ConfigParser as cp
+import configparser as cp
import json
import os
import re
@@ -11,91 +11,137 @@
import yaml
+# Environments read from config file
+# {"cloudant": {"api_key": "abc123", ...}
+# {"power": {"api_key", "cde456", ...}
+#
+ENV = {}
+
IBM_CLOUD_URL = "https://us-south.iaas.cloud.ibm.com/v1/"
IAM_URL = "https://iam.cloud.ibm.com/identity/token"
IBM_CLOUD_GENERATION = "2"
IBM_CLOUD_VERSION = "2019-08-09"
-API_KEY = None
-IAM_TOKEN = None
-SESS = requests.session()
+# Authed API request sessions, one per environment
+# {"cloudant": <Session>, ...}
+#
+SESS = {}
-def tostr(obj):
- ret = {}
- for k, v in obj.items():
- if isinstance(k, unicode):
- k = k.encode("utf-8")
- if isinstance(v, dict):
- ret[k] = tostr(v)
- elif isinstance(v, unicode):
- ret[k] = v.encode("utf-8")
- else:
- ret[k] = v
- return ret
-
-
-def load_api_key():
- global API_KEY
+def load_environment():
path = os.path.expanduser("~/.couchdb-infra-cm.cfg")
if not os.path.exists(path):
- print "Missing config file: " + path
+ print(f"Missing config file: {path}")
exit(1)
- parser = cp.SafeConfigParser()
+ parser = cp.ConfigParser()
parser.read([path])
- API_KEY = parser.get("ibmcloud", "api_key")
+
+ for section in parser.sections():
+ if not section.startswith("ibmcloud"):
+ continue
+
+ split = section.split(".")
+ if len(split) == 2:
+ (_, env) = split
+ elif len(split) == 1:
+ env = "<default>"
+ else:
+ print(f"Invalid 'ibmcloud' section {section}")
+ exit(1)
+
+ ENV[env] = {
+ "api_key" : parser.get(section, "api_key"),
+ "iam_url" : parser.get(section, "iam_url",
+ fallback=IAM_URL),
+ "api_url" : parser.get(section, "api_url",
+ fallback=IBM_CLOUD_URL),
+ "api_generation" : parser.get(section, "api_generation",
+ fallback=IBM_CLOUD_GENERATION),
+ "api_version" : parser.get(section, "api_version",
+ fallback=IBM_CLOUD_VERSION)
+ }
-def load_iam_token():
- global IAM_TOKEN
- headers = {
- "Accept": "application/json"
- }
- data = {
- "grant_type": "urn:ibm:params:oauth:grant-type:apikey",
- "apikey": API_KEY
- }
- resp = SESS.post(IAM_URL, headers=headers, data=data)
- resp.raise_for_status()
- body = resp.json()
- IAM_TOKEN = body["token_type"] + " " + body["access_token"]
- SESS.headers["Authorization"] = IAM_TOKEN
+def load_iam_tokens():
+ for env in ENV:
+ sess = requests.session()
+ headers = {
+ "Accept": "application/json"
+ }
+ data = {
+ "grant_type": "urn:ibm:params:oauth:grant-type:apikey",
+ "apikey": ENV[env]["api_key"]
+ }
+ resp = sess.post(ENV[env]["iam_url"], headers=headers, data=data)
+ resp.raise_for_status()
+ body = resp.json()
+ token = body["token_type"] + " " + body["access_token"]
+ sess.headers["Authorization"] = token
+ SESS[env] = sess
def init():
- load_api_key()
- load_iam_token()
+ load_environment()
+ load_iam_tokens()
def list_instances():
- url = IBM_CLOUD_URL + "/instances"
- params = {
- "version": IBM_CLOUD_VERSION,
- "generation": IBM_CLOUD_GENERATION,
- "limit": 100
- }
+ for env in ENV:
+ yield from list_x86_instances(env)
+ #yield from list_power_instances(env)
+
+
+def list_x86_instances(env):
+ url = ENV[env]["api_url"] + "/instances"
+ sess = SESS[env]
while url:
- resp = SESS.get(url, params=params)
+ resp = sess.get(url, params=params(env))
body = resp.json()
for instance in body["instances"]:
interface_url = instance["primary_network_interface"]["href"]
- resp = SESS.get(interface_url, params=params)
+ resp = sess.get(interface_url, params=params(env))
instance["primary_network_interface"] = resp.json()
yield instance
url = body.get("next")
+def list_power_instances(env):
+ url = ENV[env]["api_url"] + "/pvmInstances"
+ sess = SESS[env]
+ while url:
+ resp = sess.get(url, params=params(env))
+ body = resp.json()
+ for instance in body["instances"]:
+ interface_url = instance["primary_network_interface"]["href"]
+ resp = sess.get(interface_url, params=params(env))
+ instance["primary_network_interface"] = resp.json()
+ yield instance
+ url = body.get("next")
+
+
+def params(env):
+ return {
+ "version": ENV[env]["api_version"],
+ "generation" : ENV[env]["api_generation"],
+ "limit": 100
+ }
+
+
def load_bastion(bastions, instance):
if instance["status"] != "running":
return
name = instance["name"]
+ if name in bastions:
+ print(f"Duplicate bastion found {name}")
+ exit(2)
+
ip_addr = None
net_iface = instance["primary_network_interface"]
floating_ips = net_iface.get("floating_ips", [])
if not floating_ips:
- print "Bastion is missing a public IP: %s" % name
+ print(f"Bastion is missing a public IP: {name}")
exit(2)
ip_addr = floating_ips[0]["address"]
@@ -126,6 +172,9 @@
return
name = instance["name"]
+ if name in ci_agents:
+ print(f"Duplicate ci_agent found {name}")
+ exit(2)
net_iface = instance["primary_network_interface"]
ci_agents[name] = {
@@ -189,7 +238,7 @@
}}
with open(fname, "w") as handle:
- yaml.dump(tostr(inventory), stream=handle, default_flow_style=False)
+ yaml.dump(inventory, stream=handle, default_flow_style=False)
def write_ssh_cfg(filename, bastions, ci_agents):
@@ -251,6 +300,7 @@
)
return parser.parse_args()
+
def main():
args = parse_args()