copr-be: helper script to start/stop VMs in IBM Cloud
This commit is contained in:
parent
6436ffc139
commit
e451780549
3 changed files with 213 additions and 1 deletions
|
@ -55,10 +55,11 @@
|
||||||
- provision_config
|
- provision_config
|
||||||
|
|
||||||
- name: resalloc, scripts
|
- name: resalloc, scripts
|
||||||
template: src="resalloc/{{ item }}" dest="/var/lib/resallocserver/resalloc_provision/"
|
template: src="resalloc/{{ item }}.j2" dest="/var/lib/resallocserver/resalloc_provision/"
|
||||||
mode=755
|
mode=755
|
||||||
with_items:
|
with_items:
|
||||||
- vm-release
|
- vm-release
|
||||||
|
- ibm-cloud-vm
|
||||||
tags:
|
tags:
|
||||||
- provision_config
|
- provision_config
|
||||||
|
|
||||||
|
@ -143,3 +144,13 @@
|
||||||
|
|
||||||
- name: sometimes it is worth having the copr.py ansible plugin
|
- name: sometimes it is worth having the copr.py ansible plugin
|
||||||
package: name=ansible-collection-community-general state=latest
|
package: name=ansible-collection-community-general state=latest
|
||||||
|
|
||||||
|
- name: install IBM Cloud token file
|
||||||
|
copy:
|
||||||
|
content: "IBMCLOUD_API_KEY={{ copr_cloud_ibm_token | default('unset') }}"
|
||||||
|
dest: /var/lib/resallocserver/.ibm-cloud-token
|
||||||
|
owner: resalloc
|
||||||
|
group: resalloc
|
||||||
|
mode: 0600
|
||||||
|
tags:
|
||||||
|
- provision_config
|
||||||
|
|
201
roles/copr/backend/templates/resalloc/ibm-cloud-vm.js
Executable file
201
roles/copr/backend/templates/resalloc/ibm-cloud-vm.js
Executable file
|
@ -0,0 +1,201 @@
|
||||||
|
#! /usr/bin/python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Start a new VM in IBM Cloud under the copr-team account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import pipes
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from ibm_vpc import VpcV1
|
||||||
|
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
|
||||||
|
from ibm_cloud_sdk_core import ApiException
|
||||||
|
|
||||||
|
DEFAULT_PLAYBOOK = "{{ provision_directory }}/libvirt-provision.yml"
|
||||||
|
DEFAULT_TOKEN_FILE = "/var/lib/resallocserver/.ibm-cloud-token"
|
||||||
|
|
||||||
|
subnet_id = "02f7-98674f68-aae1-4ea1-a889-5a0b7a07f4b8"
|
||||||
|
image_id = "r022-ce9f9b97-1d1c-46d0-9699-6a1c7e6b2e45"
|
||||||
|
vpc_id = "r022-8438169e-d881-4bda-b603-d31fdf0f8b3a"
|
||||||
|
security_group_id = "r022-bf49b90e-c00f-4c68-8707-2936b47b286b"
|
||||||
|
my_key_id = "r022-3918e368-8e00-4e23-9119-5e3ce1eb33bd"
|
||||||
|
profile = "cz2-2x4"
|
||||||
|
zone_name = "jp-tok-2"
|
||||||
|
|
||||||
|
|
||||||
|
def bind_floating_ip(service, instance_id, log):
|
||||||
|
"""
|
||||||
|
Assign an existing Floating IP to given instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
log.info("Bind floating IP")
|
||||||
|
response_list = service.list_floating_ips().get_result()['floating_ips']
|
||||||
|
floating_ip_id = None
|
||||||
|
for item in response_list:
|
||||||
|
log.info("%s\t%s\t%s", item['id'], item['name'], item['status'])
|
||||||
|
if item["status"] == "available":
|
||||||
|
floating_ip_id = item['id']
|
||||||
|
floating_ip_address = item['address']
|
||||||
|
break
|
||||||
|
if floating_ip_id is None:
|
||||||
|
log.error("No available floating IP")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
response_list = service.list_instance_network_interfaces(instance_id)
|
||||||
|
response_list = response_list.get_result()['network_interfaces']
|
||||||
|
log.info(response_list)
|
||||||
|
for item in response_list:
|
||||||
|
log.info("{}\t{}".format(item['id'], item['name']))
|
||||||
|
network_interface_id = response_list[0]['id']
|
||||||
|
log.info("Network interface ID: {}".format(network_interface_id))
|
||||||
|
service.add_instance_network_interface_floating_ip(
|
||||||
|
instance_id,
|
||||||
|
network_interface_id,
|
||||||
|
floating_ip_id,
|
||||||
|
)
|
||||||
|
log.info("Floating IP: %s", floating_ip_address)
|
||||||
|
return floating_ip_address
|
||||||
|
|
||||||
|
|
||||||
|
def run_playbook(host, opts):
|
||||||
|
"""
|
||||||
|
Run ansible-playbook against the given hostname
|
||||||
|
"""
|
||||||
|
cmd = ["ansible-playbook", opts.playbook, "--inventory", "{},".format(host)]
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def create_instance(service, instance_name, opts, log):
|
||||||
|
"""
|
||||||
|
Start the VM, name it "instance_name"
|
||||||
|
"""
|
||||||
|
instance_prototype_model = {
|
||||||
|
"keys": [{"id": my_key_id}],
|
||||||
|
"name": instance_name,
|
||||||
|
"profile": {"name": profile},
|
||||||
|
"vpc": {
|
||||||
|
"id": vpc_id,
|
||||||
|
},
|
||||||
|
"boot_volume_attachment": {
|
||||||
|
"volume": {
|
||||||
|
"name": instance_name + "-root",
|
||||||
|
"profile": {
|
||||||
|
"name": "general-purpose",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"delete_volume_on_instance_delete": True,
|
||||||
|
},
|
||||||
|
"image": {"id": image_id},
|
||||||
|
"primary_network_interface": {
|
||||||
|
'name': 'primary-network-interface',
|
||||||
|
'subnet': {
|
||||||
|
"id": subnet_id,
|
||||||
|
},
|
||||||
|
"security_groups": [
|
||||||
|
{"id": security_group_id},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"zone": {
|
||||||
|
"name": zone_name,
|
||||||
|
},
|
||||||
|
"volume_attachments": [{
|
||||||
|
"volume": {
|
||||||
|
"name": instance_name + "-swap",
|
||||||
|
"capacity": 168,
|
||||||
|
"profile": {"name": "general-purpose"},
|
||||||
|
},
|
||||||
|
"delete_volume_on_instance_delete": True,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_created = None
|
||||||
|
try:
|
||||||
|
response = service.create_instance(instance_prototype_model)
|
||||||
|
instance_created = instance_name
|
||||||
|
log.debug("Instance response: %s", response)
|
||||||
|
log.debug("Instance response[get_result]: %s", response.get_result())
|
||||||
|
instance_id = response.get_result()['id']
|
||||||
|
log.info("Instance ID: %s", instance_id)
|
||||||
|
ip_address = bind_floating_ip(service, instance_id, log)
|
||||||
|
_wait_for_ssh(ip_address)
|
||||||
|
run_playbook(ip_address, opts)
|
||||||
|
# Tell the Resalloc clients how to connect to this instance.
|
||||||
|
print(ip_address)
|
||||||
|
except:
|
||||||
|
if instance_created:
|
||||||
|
delete_instance(service, instance_name, log)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def delete_instance(service, instance_name, log):
|
||||||
|
""" Delete instance by it's name """
|
||||||
|
log.info("Deleting instance %s", instance_name)
|
||||||
|
response_list = service.list_instances().get_result()['instances']
|
||||||
|
delete_instance_id = None
|
||||||
|
for item in response_list:
|
||||||
|
log.debug("Available: %s %s %s", item['id'], item['name'], item['status'])
|
||||||
|
if instance_name == item['name']:
|
||||||
|
delete_instance_id = item['id']
|
||||||
|
if delete_instance_id is None:
|
||||||
|
log.error("Could not find instance {}".format(instance_name))
|
||||||
|
sys.exit(1)
|
||||||
|
service.delete_instance(delete_instance_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_arg_parser():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--token-file", default=DEFAULT_TOKEN_FILE)
|
||||||
|
parser.add_argument("--log-level", default="info")
|
||||||
|
subparsers = parser.add_subparsers(dest='subparser')
|
||||||
|
parser_create = subparsers.add_parser(
|
||||||
|
"create", help="Create an instance in IBM Cloud")
|
||||||
|
parser_create.add_argument("name")
|
||||||
|
parser_create.add_argument("--playbook", default=DEFAULT_PLAYBOOK)
|
||||||
|
parser_delete = subparsers.add_parser(
|
||||||
|
"delete", help="Delete instance by it's name from IBM Cloud")
|
||||||
|
parser_delete.add_argument("name")
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_ssh(floating_ip):
|
||||||
|
cmd = ["resalloc-aws-wait-for-ssh",
|
||||||
|
"--log", "debug",
|
||||||
|
"--timeout", "240",
|
||||||
|
floating_ip]
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def _main():
|
||||||
|
opts = _get_arg_parser().parse_args()
|
||||||
|
log_level = getattr(logging, opts.log_level.upper())
|
||||||
|
logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
|
||||||
|
log = logging.getLogger()
|
||||||
|
|
||||||
|
cmd = "source {} ; echo $IBMCLOUD_API_KEY".format(
|
||||||
|
pipes.quote(opts.token_file))
|
||||||
|
output = subprocess.check_output(cmd, shell=True)
|
||||||
|
token = output.decode("utf-8").strip().rsplit("\n", maxsplit=1)[-1]
|
||||||
|
authenticator = IAMAuthenticator(token)
|
||||||
|
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
service = VpcV1(now.strftime('%Y-%m-%d'), authenticator=authenticator)
|
||||||
|
|
||||||
|
# We work with Tokyo only for now.
|
||||||
|
service.set_service_url("https://jp-tok.iaas.cloud.ibm.com/v1")
|
||||||
|
|
||||||
|
if opts.subparser == "create":
|
||||||
|
create_instance(service, opts.name, opts, log)
|
||||||
|
elif opts.subparser == "delete":
|
||||||
|
delete_instance(service, opts.name, log)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
_main()
|
||||||
|
|
||||||
|
|
||||||
|
# vi: ft=python
|
Loading…
Add table
Add a link
Reference in a new issue