Skip to content

Instantly share code, notes, and snippets.

@EnriqCG
Created September 9, 2019 11:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EnriqCG/1cb7ed00889c69fdd07f0b5312cba681 to your computer and use it in GitHub Desktop.
Save EnriqCG/1cb7ed00889c69fdd07f0b5312cba681 to your computer and use it in GitHub Desktop.
Openstack client for creating and destroying instances
"""Openstack client API for creating and destroying instances with the Nova API
Date: 04/09/2019
Author: Enrique Carpintero
"""
import requests
import json
class OpenStackAPI:
def __init__(self, api_host):
self.host = api_host
def authenticate(self, user_id, password, project_id):
"""Authenticates with the password method on Keystone
and saves the token on the object for later use.
"""
r = requests.post("{}:5000/v3/auth/tokens".format(self.host), json= {
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"id": user_id,
"password": password
}
}
},
"scope": {
"project": {
"id": project_id
}
}
}
})
#
try:
self.headers = {
'X-Auth-Token': r.headers['X-Subject-Token'],
'Content-Type': 'application/json'
}
except KeyError:
print("Could not get token from Keystone")
raise SystemExit
def get_instances(self):
"""Returns a list of all available instances
Reference: https://docs.openstack.org/api-ref/compute/?expanded=show-server-details-detail#list-servers
"""
r = requests.get("{}:8774/v2.1/servers".format(self.host), headers=self.headers)
return r.json()
def get_instance_data_by_id(self, instance_id):
"""Returns instance data provided by Openstack
Reference: https://docs.openstack.org/api-ref/compute/?expanded=show-server-details-detail#show-server-details
"""
r = requests.get("{}:8774/v2.1/servers/{}".format(self.host, instance_id), headers=self.headers)
return r.json()
def get_instance_ip(self, instance_id):
"""Return the instance IP as a string. Returns False after 100 failed attempts.
Hammers the API on a loop until Neutron (network) allocates an IP.
"""
for _ in range(100):
try:
instance = self.get_instance_data_by_id(instance_id)
return instance['server']['addresses']['sharednet1'][0]['addr']
except KeyError:
continue
return False
def create_instance(self, instance_name, image_id, flavor_id, network_id, security_group_name = None, user_data = None):
"""Nova API reference: https://docs.openstack.org/api-ref/compute/?expanded=create-server-detail#create-server
image_id, flavor_id and network_id can be obtained through Horizon
config_drive should always be True
user_data is a base64 encoded string for the cloud-init
"""
payload = {
"server": {
"name": instance_name,
"imageRef": image_id,
"flavorRef": flavor_id,
"networks": [{
"uuid": network_id
}],
"config_drive": True
}
}
if security_group_name is not None:
payload['server']['security_groups'] = [{ "name": security_group_name }]
if user_data is not None:
payload['server']['user_data'] = user_data
r = requests.post("{}:8774/v2.1/servers".format(self.host), headers=self.headers, json= payload)
# Status code for resource creation is 202 Accepted
if r.status_code is not 202:
print("Could not create Nova instance", r.json())
return r.json()
def delete_instance_by_id(self, instance_id):
"""Returns True if instance deletion is successful. False if unsuccessful
"""
r = requests.delete("{}:8774/v2.1/servers/{}".format(self.host, instance_id), headers=self.headers)
# 204 No Content - request was successful
return True if r.status_code is 204 else False
def delete_all_instances(self):
"""Iterates over created instances and executes a delete for each one of them
Once that is completed it checks if all instances were in fact deleted.
"""
instances = self.get_instances()
for instance in instances['servers']:
if self.delete_instance_by_id(instance['id']) is not True:
print("Could not delete instance with name: {}".format(instance['name']))
instances = self.get_instances()
return True
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment