Skip to content

Commit 825fc91

Browse files
committed
Refactored client to use a mixin pattern, added server specific function versioning
1 parent 8e8a113 commit 825fc91

File tree

11 files changed

+597
-358
lines changed

11 files changed

+597
-358
lines changed

stackdio/client/__init__.py

Lines changed: 19 additions & 336 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
import json
22
import logging
3-
import os
43

5-
from .http import HttpMixin, use_admin_auth, endpoint
4+
from .http import use_admin_auth, endpoint
65
from .exceptions import StackException
76

7+
from .blueprint import BlueprintMixin
8+
from .formula import FormulaMixin
9+
from .profile import ProfileMixin
10+
from .provider import ProviderMixin
11+
from .region import RegionMixin
12+
from .settings import SettingsMixin
13+
from .stack import StackMixin
14+
15+
from .version import _parse_version_string
16+
817
logger = logging.getLogger(__name__)
918

1019

11-
class StackdIO(HttpMixin):
20+
class StackdIO(BlueprintMixin, FormulaMixin, ProfileMixin,
21+
ProviderMixin, RegionMixin, StackMixin, SettingsMixin):
1222

13-
def __init__(self,
14-
protocol="https",
15-
host="localhost",
16-
port=443,
17-
base_url=None,
18-
auth=None,
19-
auth_admin=None,
23+
def __init__(self, protocol="https", host="localhost", port=443,
24+
base_url=None, auth=None, auth_admin=None,
2025
verify=False):
2126
"""auth_admin is optional, only needed for creating provider, profile,
2227
and base security groups"""
@@ -33,106 +38,12 @@ def __init__(self,
3338
self.auth = auth
3439
self.auth_admin = auth_admin
3540

41+
_, self.version = _parse_version_string(self.get_version())
3642

37-
@use_admin_auth
38-
@endpoint("providers/")
39-
def create_provider(self, **kwargs):
40-
"""Create a provider"""
41-
42-
form_data = {
43-
"title": None,
44-
"account_id": None,
45-
"provider_type": None,
46-
"access_key_id": None,
47-
"secret_access_key": None,
48-
"keypair": None,
49-
"security_groups": None,
50-
"route53_domain": None,
51-
"default_availability_zone": None,
52-
"private_key": None
53-
}
54-
55-
for key in form_data.keys():
56-
form_data[key] = kwargs.get(key)
57-
58-
return self._post(endpoint, data=json.dumps(form_data), jsonify=True)
59-
60-
61-
@endpoint("providers/")
62-
def get_provider(self, title, provider_name):
63-
"""Look for and return a provider"""
64-
65-
result = self._get(endpoint, jsonify=True)
66-
for provider in result['results']:
67-
if provider.get("title") == title and \
68-
provider.get("provider_type_name") == provider_name:
69-
70-
return provider
71-
72-
raise StackException("Provider %s not found" % title)
73-
74-
75-
@use_admin_auth
76-
@endpoint("providers/")
77-
def create_profile(self, title, image_id, ssh_user, cloud_provider,
78-
default_instance_size=None):
79-
"""Create a profile"""
80-
data = {
81-
"title": title,
82-
"image_id": image_id,
83-
"ssh_user": ssh_user,
84-
"cloud_provider": cloud_provider,
85-
"default_instance_size": default_instance_size
86-
}
87-
return self._post(endpoint, data=json.dumps(data), jsonify=True)
88-
89-
90-
@endpoint("profiles/")
91-
def get_profile(self, title, cloud_provider):
92-
"""Look for and return a profile"""
93-
94-
result = self._get(endpoint, jsonify=True)
95-
96-
for profile in result['results']:
97-
if profile.get("title") == title and \
98-
profile.get("cloud_provider") == cloud_provider:
99-
return profile
100-
101-
return None
102-
103-
104-
@endpoint("formulas/")
105-
def import_formula(self, formula_uri, public=True):
106-
"""Import a formula"""
107-
data = {
108-
"uri": formula_uri,
109-
"public": public,
110-
}
111-
return self._post(endpoint, data=json.dumps(data), jsonify=True)
112-
113-
114-
@endpoint("blueprints/")
115-
def create_blueprint(self, blueprint, provider="ec2"):
116-
"""Create a blueprint"""
117-
118-
# check the provided blueprint to see if we need to look up any ids
119-
for host in blueprint["hosts"]:
120-
if isinstance(host["size"], str):
121-
host["size"] = self.get_instance_id(host["size"], provider)
12243

123-
if isinstance(host["zone"], str):
124-
host["zone"] = self.get_zone(host["zone"], provider)
125-
126-
if isinstance(host["cloud_profile"], str):
127-
host["cloud_profile"] = self.get_profile_id(host["cloud_profile"])
128-
129-
for component in host["formula_components"]:
130-
if isinstance(component["id"], (tuple, list)):
131-
component["id"] = self.get_component_id(
132-
self.get_formula(component["id"][0]),
133-
component["id"][1])
134-
135-
return self._post(endpoint, data=json.dumps(blueprint), jsonify=True)
44+
@endpoint("version/")
45+
def get_version(self):
46+
return self._get(endpoint, jsonify=True)['version']
13647

13748

13849
@use_admin_auth
@@ -149,149 +60,6 @@ def create_security_group(self, name, description, cloud_provider, is_default=Tr
14960
return self._post(endpoint, data=json.dumps(data), jsonify=True)
15061

15162

152-
@endpoint("settings/")
153-
def set_public_key(self, public_key):
154-
"""Upload a public key for our user. public_key can be the actual key, a
155-
file handle, or a path to a key file"""
156-
157-
if isinstance(public_key, file):
158-
public_key = public_key.read()
159-
elif isinstance(public_key, str) and os.path.exists(public_key):
160-
public_key = open(public_key, "r").read()
161-
162-
data = {
163-
"public_key": public_key
164-
}
165-
return self._put(endpoint, data=json.dumps(data), jsonify=True)
166-
167-
168-
@endpoint("formulas/")
169-
def get_formulas(self):
170-
"""Return all formulas"""
171-
return self._get(endpoint, jsonify=True)['results']
172-
173-
174-
def get_formula(self, title):
175-
"""Get a formula that matches title"""
176-
177-
for formula in self.get_formulas():
178-
if formula.get("title") == title:
179-
return formula
180-
181-
raise StackException("Formula %s not found" % title)
182-
183-
184-
def get_formula_id(self, title):
185-
"""Get the id for a formula that matches title. If component_title is
186-
provided, find and return the component id"""
187-
188-
formula = self.get_formula(title)
189-
return formula.get("id")
190-
191-
192-
def get_component_id(self, formula, component_title):
193-
"""Get the id for a component from formula_id that matches title"""
194-
195-
for component in formula.get("components"):
196-
if component.get("title") == component_title:
197-
return component.get("id")
198-
199-
raise StackException("Component %s not found for formula %s" %
200-
(component_title, formula.get("title")))
201-
202-
203-
@endpoint("providers/")
204-
def get_provider_id(self, slug, title=False):
205-
"""Get the id for a provider that matches slug. If title is True will
206-
look at title instead."""
207-
208-
result = self._get(endpoint, jsonify=True)
209-
210-
for provider in result['results']:
211-
if provider.get("slug" if not title else "title") == slug:
212-
return provider.get("id")
213-
214-
raise StackException("Provider %s not found" % slug)
215-
216-
217-
@endpoint("profiles/")
218-
def get_profile_id(self, slug, title=False):
219-
"""Get the id for a profile that matches slug. If title is True will look
220-
at title instead."""
221-
222-
result = self._get(endpoint, jsonify=True)
223-
224-
for profile in result['results']:
225-
if profile.get("slug" if not title else "title") == slug:
226-
return profile.get("id")
227-
228-
return StackException("Provider %s not found" % slug)
229-
230-
231-
@endpoint("stacks/")
232-
def get_stacks(self):
233-
"""Return a list of all stacks"""
234-
return self._get(endpoint, jsonify=True)['results']
235-
236-
237-
@endpoint("stacks/{stack_id}/")
238-
def get_stack(self, stack_id):
239-
"""Get stack info"""
240-
result = self._get(endpoint, none_on_404=True, jsonify=True)
241-
if result is None:
242-
raise StackException("Stack %s not found" % stack_id)
243-
else:
244-
return result
245-
246-
247-
@endpoint("stacks/{stack_id}/history/")
248-
def get_stack_history(self, stack_id):
249-
"""Get stack info"""
250-
result = self._get(endpoint, none_on_404=True, jsonify=True)
251-
if result is None:
252-
raise StackException("Stack %s not found" % stack_id)
253-
else:
254-
return result
255-
256-
257-
@endpoint("stacks/")
258-
def get_stack_id(self, title):
259-
"""Find a stack id"""
260-
261-
result = self._get(endpoint, jsonify=True)
262-
try:
263-
for stack in result['results']:
264-
if stack.get("title") == title:
265-
return stack.get("id")
266-
except TypeError, e:
267-
logger.error("Error querying stacks: %s", e)
268-
269-
raise StackException("Stack %s not found" % title)
270-
271-
272-
@endpoint("stacks/{stack_id}/hosts/")
273-
def get_stack_hosts(self, stack_id):
274-
"""Get a list of all stack hosts"""
275-
return self._get(endpoint, jsonify=True)['results']
276-
277-
278-
@endpoint("blueprints/{blueprint_id}/")
279-
def get_blueprint(self, blueprint_id):
280-
"""Return info for a specific blueprint_id"""
281-
return self._get(endpoint, jsonify=True)
282-
283-
284-
@endpoint("blueprints/")
285-
def get_blueprint_id(self, title):
286-
"""Get the id for a blueprint that matches title"""
287-
result = self._get(endpoint, params={"title": title}, jsonify=True)
288-
289-
if not result.get('count') == 1:
290-
raise StackException("Blueprint %s not found" % title)
291-
292-
return result['results'][0]['id']
293-
294-
29563
@endpoint("instance_sizes/")
29664
def get_instance_id(self, instance_id, provider_type="ec2"):
29765
"""Get the id for an instance_id. The instance_id parameter is the
@@ -306,88 +74,3 @@ def get_instance_id(self, instance_id, provider_type="ec2"):
30674

30775
raise StackException("Instance type %s from provider %s not found" %
30876
(instance_id, provider_type))
309-
310-
311-
@endpoint("provider_types/")
312-
def get_provider_type(self, type_name):
313-
"""Get the id for the provider specified by type_name"""
314-
315-
result = self._get(endpoint, jsonify=True)
316-
for provider_type in result['results']:
317-
if provider_type.get("type_name") == type_name:
318-
return provider_type.get("id")
319-
320-
raise StackException("Provider type %s not found" % type_name)
321-
322-
323-
@endpoint("zones/")
324-
def get_zone(self, title, type_name="ec2"):
325-
"""Get a zone id for title"""
326-
327-
provider_type = self.get_provider_type(type_name)
328-
result = self._get(endpoint, jsonify=True)
329-
for zone in result['results']:
330-
if zone.get("title") == title and \
331-
zone.get("provider_type") == provider_type:
332-
return zone.get("id")
333-
334-
raise StackException("Zone %s not found for %s" % (title, type_name))
335-
336-
337-
@endpoint("stacks/")
338-
def launch_stack(self, stack_data):
339-
"""Launch a stack as described by stack_data"""
340-
return self._post(endpoint, data=json.dumps(stack_data), jsonify=True)
341-
342-
343-
@endpoint("stacks/{stack_id}/hosts/")
344-
def describe_hosts(self, stack_id, key="fqdn", ec2=False):
345-
"""Retrieve a list of info about a stack. Defaults to the id for each
346-
host, but you can specify any available key. Setting ec2=True will
347-
force it to inspect the ec2_metadata field."""
348-
349-
EC2 = "ec2_metadata"
350-
result = self._get(endpoint, jsonify=True)
351-
352-
stack_details = []
353-
354-
for host in result['results']:
355-
if not ec2:
356-
host_details = host.get(key)
357-
else:
358-
host_details = host.get(EC2).get(key)
359-
360-
if host_details:
361-
stack_details.append(host_details)
362-
363-
if stack_details:
364-
return stack_details
365-
366-
raise StackException("Key %s for stack %s not available" % (key, stack_id))
367-
368-
369-
@endpoint("stacks/{stack_id}/")
370-
def delete_stack(self, stack_id):
371-
"""Destructively delete a stack forever."""
372-
# make sure the stack exists
373-
self.get_stack(stack_id)
374-
return self._delete(endpoint, jsonify=True)
375-
376-
377-
@endpoint("stacks/{stack_id}/action/")
378-
def get_valid_actions(self, stack_id):
379-
return self._get(endpoint, jsonify=True)['available_actions']
380-
381-
382-
@endpoint("stacks/{stack_id}/action/")
383-
def do_action(self, stack_id, action):
384-
"""Execute an action on a stack"""
385-
valid_actions = self.get_valid_actions(stack_id)
386-
387-
if action not in valid_actions:
388-
raise StackException("Invalid action, must be one of %s" %
389-
", ".join(valid_actions))
390-
391-
data = {"action": action}
392-
393-
return self._post(endpoint, data=json.dumps(data), jsonify=True)

0 commit comments

Comments
 (0)