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
|
||||
|
||||
- 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
|
||||
with_items:
|
||||
- vm-release
|
||||
- ibm-cloud-vm
|
||||
tags:
|
||||
- provision_config
|
||||
|
||||
|
@ -143,3 +144,13 @@
|
|||
|
||||
- name: sometimes it is worth having the copr.py ansible plugin
|
||||
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