Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve python example #3

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
10 changes: 8 additions & 2 deletions python/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
FROM python:2-onbuild
FROM python:3.4

MAINTAINER Kai Davenport <[email protected]>
CMD [ "python", "./example.py" ]
MAINTAINER Srdjan Grubor <[email protected]>

ADD ./example.py .
RUN chmod +x ./example.py

CMD [ "./example.py" ]
214 changes: 171 additions & 43 deletions python/example.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,60 +1,188 @@
import httplib
#!/usr/bin/env python3

import http.client
import json
import os
import ssl
import tempfile

# NOTE - to run this example, you need at least python 2.7.9
class FlockerApi(object):
DEFAULT_PLUGIN_DIR = os.environ.get('CERT_DIR', '/etc/flocker')

def __init__(self, api_version = 1, debug = False):
control_service = os.environ.get("CONTROL_SERVICE", "localhost")
control_port = os.environ.get("CONTROL_PORT", 4523)

self._api_version = api_version
self._debug = debug
self._last_known_config = None

key_file = os.environ.get("KEY_FILE", "%s/plugin.key" % self.DEFAULT_PLUGIN_DIR)
cert_file = os.environ.get("CERT_FILE", "%s/plugin.crt" % self.DEFAULT_PLUGIN_DIR)
ca_file = os.environ.get("CA_FILE", "%s/cluster.crt" % self.DEFAULT_PLUGIN_DIR)

# Create a certificate chain and then pass that into the SSL system.
cert_with_chain_tempfile = tempfile.NamedTemporaryFile()

temp_cert_with_chain_path = cert_with_chain_tempfile.name
os.chmod(temp_cert_with_chain_path, 0o0600)

# Write our cert and append the CA cert to build the chain
with open(cert_file, 'rb') as cert_file_obj:
cert_with_chain_tempfile.write(cert_file_obj.read())

cert_with_chain_tempfile.write('\n'.encode('utf-8'))

with open(ca_file, 'rb') as cacert_file_obj:
cert_with_chain_tempfile.write(cacert_file_obj.read())

# Reset file pointer for the SSL context to read it properly
cert_with_chain_tempfile.seek(0)

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.load_cert_chain(temp_cert_with_chain_path, key_file)

self._http_client = http.client.HTTPSConnection(control_service,
control_port,
context=ssl_context)

# XXX: These should really be generic functions created dynamically
def get(self, endpoint, data = None):
return self._make_api_request('GET',
"/v%s/%s" % (self._api_version, endpoint),
data)

def post(self, endpoint, data = None):
return self._make_api_request('POST',
"/v%s/%s" % (self._api_version, endpoint),
data)

def delete(self, endpoint, data = None):
return self._make_api_request('DELETE',
"/v%s/%s" % (self._api_version, endpoint),
data)

def _make_api_request(self, method, endpoint, data = None):
# Convert data to string if it's not yet in this format
if data and not isinstance(data, str):
data = json.dumps(data).encode('utf-8')

headers = { 'Content-type': 'application/json' }
if self._last_known_config:
headers['X-If-Configuration-Matches'] = self._last_known_config

self._http_client.request(method, endpoint, data,
headers=headers)

response = self._http_client.getresponse()

status = response.status
body = response.read()

# Make sure to use the X
if 'X-Configuration-Tag' in response.getheaders():
self._last_known_config = response.getheaders()['X-Configuration-Tag'].decode('utf-8')

print('Status:', status)

# If you want verbose debugging
# print('Body:', body)

print()

result = json.loads(body.decode('utf-8'))

if self._debug == True:
print(json.dumps(result, sort_keys=True, indent=4))

return result

# Specific API requests
def get_version(self):
version = self.get('version')
return version['flocker']

def create_volume(self, name, size_in_gb, primary_id, profile = None):
if not isinstance(size_in_gb, int):
print('Error! Size must be an integer!')
exit(1)

data = {
'primary': primary_id,
'maximum_size': size_in_gb << 30,
'metadata': {
'name': name
}
}

if profile:
data['metadata']['clusterhq:flocker:profile'] = profile

return self.post('configuration/datasets', data)

def move_volume(self, volume_id, new_primary_id):
data = { 'primary': new_primary_id }
return self.post('configuration/datasets/%s' % volume_id, data)

def delete_volume(self, dataset_id):
return self.delete('configuration/datasets/%s' % dataset_id)

def get_volumes(self):
return self.get('configuration/datasets')

def get_nodes(self):
return self.get('state/nodes')

def get_leases(self):
return self.get('configuration/leases')

# Define the control IP, port, and the certificates for authentication.
def release_lease(self, dataset_id):
return self.delete('configuration/leases/%s' % dataset_id)

CONTROL_SERVICE = os.environ.get(
"CONTROL_SERVICE", "54.157.8.57")
CONTROL_PORT = os.environ.get(
"CONTROL_PORT", 4523)
KEY_FILE = os.environ.get(
"KEY_FILE", "/Users/kai/projects/flocker-api-examples/flockerdemo.key")
CERT_FILE = os.environ.get(
"CERT_FILE", "/Users/kai/projects/flocker-api-examples/flockerdemo.crt")
CA_FILE = os.environ.get(
"CA_FILE", "/Users/kai/projects/flocker-api-examples/cluster.crt")
def acquire_lease(self, dataset_id, node_id, expires = None):
data = { 'dataset_id': dataset_id,
'node_uuid': node_id,
'expires': expires }
return self.post('configuration/leases', data)

# Create a certificate chain and then pass that into the SSL system.
if __name__ == '__main__':
api = FlockerApi(debug = True)

certtemp = tempfile.NamedTemporaryFile()
TEMP_CERT_CA_FILE = certtemp.name
os.chmod(TEMP_CERT_CA_FILE, 0600)
certtemp.write(open(CERT_FILE).read())
certtemp.write("\n")
certtemp.write(open(CA_FILE).read())
certtemp.seek(0)
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.load_cert_chain(TEMP_CERT_CA_FILE, KEY_FILE)
# Show us the version of Flocker
print("Version:", api.get_version())

# Finally, create a HTTP connection.
# Get current volumes (datasets)
print('Datasets:')
datasets = api.get_volumes()

c = httplib.HTTPSConnection(CONTROL_SERVICE, CONTROL_PORT, context=ctx)
print('Nodes:')
nodes = api.get_nodes()
first_node = nodes[0]['uuid']

def make_api_request(method, endpoint, data=None):
if method in ("GET", "DELETE"):
c.request(method, endpoint)
elif method == "POST":
c.request("POST", endpoint, data,
headers={"Content-type": "application/json"})
else:
raise Exception("Unknown method %s" % (method,))
print('Trying to reuse the primary from returned list')
primary_id = datasets[0]['primary']
print('Primary:', primary_id)

r = c.getresponse()
body = r.read()
status = r.status
print('Create volume:')
# Create a Flocker volume of size 10GB
dataset_create_result = api.create_volume('my-test-volume3',
10,
primary_id,
profile = "gold")
volume_id = dataset_create_result['dataset_id']

print body
print('Move volume (to the same node):')
api.move_volume(volume_id, primary_id)

# Make the first request to check the service is working.
print('Acquire lease:')
api.acquire_lease(volume_id, first_node)

make_api_request("GET", "/v1/version")
print('Leases:')
leases = api.get_leases()
lease_id = leases[0]

# Create a volume.
print('Release lease:')
api.release_lease(volume_id)

make_api_request("POST", "/v1/configuration/datasets",
data= r'{"primary": "%s", "maximum_size": 107374182400, "metadata": {"name": "example_dataset"}}'
% ("5540d6e3-392b-4da0-828a-34b724c5bb80",))
print('Delete volume:')
api.delete_volume(volume_id)
Loading