# -*- coding: utf-8 -*-
import json
import os
import requests
import attr
from cfme.utils.version import get_stream
from cfme.utils.appliance import current_appliance, IPAppliance
from cfme.utils.conf import credentials, env
# TODO: use custom wait_for logger fitting sprout
from cfme.utils.log import logger
from cfme.utils.wait import wait_for
[docs]class SproutException(Exception):
pass
[docs]class AuthException(SproutException):
pass
[docs]@attr.s
class APIMethodCall(object):
_client = attr.ib()
_method_name = attr.ib()
def __call__(self, *args, **kwargs):
return self._client.call_method(self._method_name, *args, **kwargs)
[docs]@attr.s
class SproutClient(object):
_proto = attr.ib(default="http")
_host = attr.ib(default="localhost")
_port = attr.ib(default=8000)
_entry = attr.ib(default="appliances/api")
_auth = attr.ib(default=None)
@property
def api_entry(self):
return "{}://{}:{}/{}".format(self._proto, self._host, self._port, self._entry)
def _post(self, **data):
return requests.post(self.api_entry, data=json.dumps(data))
def _call_post(self, **data):
"""Protect from the Sprout being updated (error 502,503)"""
result = wait_for(
lambda: self._post(**data),
num_sec=60,
fail_condition=lambda r: r.status_code in {502, 503},
delay=2,
)
return result.out.json()
[docs] def call_method(self, name, *args, **kwargs):
req_data = {
"method": name,
"args": args,
"kwargs": kwargs,
}
logger.info("SPROUT: Called {} with {} {}".format(name, args, kwargs))
if self._auth is not None:
req_data["auth"] = self._auth
result = self._call_post(**req_data)
try:
if result["status"] == "exception":
raise SproutException(
"Exception {} raised! {}".format(
result["result"]["class"], result["result"]["message"]))
elif result["status"] == "autherror":
raise AuthException(
"Authentication failed! {}".format(result["result"]["message"]))
else:
return result["result"]
except KeyError:
raise Exception("Malformed response from Sprout!")
def __getattr__(self, attr):
return APIMethodCall(self, attr)
[docs] @classmethod
def from_config(cls, **kwargs):
host = env.get("sprout", {}).get("hostname", "localhost")
port = env.get("sprout", {}).get("port", 8000)
user = os.environ.get("SPROUT_USER", credentials.get("sprout", {}).get("username"))
password = os.environ.get(
"SPROUT_PASSWORD", credentials.get("sprout", {}).get("password"))
if user and password:
auth = user, password
else:
auth = None
return cls(host=host, port=port, auth=auth, **kwargs)
[docs] def provision_appliances(
self, count=1, preconfigured=False, version=None, stream=None, provider=None,
provider_type=None, lease_time=120, ram=None, cpu=None):
# If we specify version, stream is ignored because we will get that specific version
if version:
stream = get_stream(version)
# If we specify stream but not version, sprout will give us latest version of that stream
elif stream:
pass
# If we dont specify either, we will get the same version as current appliance
else:
stream = get_stream(current_appliance.version)
version = current_appliance.version.vstring
request_id = self.call_method(
'request_appliances', preconfigured=preconfigured, version=version,
provider_type=provider_type, group=stream, provider=provider, lease_time=lease_time,
ram=ram, cpu=cpu, count=count
)
wait_for(
lambda: self.call_method('request_check', str(request_id))['finished'], num_sec=300,
message='provision {} appliance(s) from sprout'.format(count))
data = self.call_method('request_check', str(request_id))
logger.debug(data)
appliances = []
for appliance in data['appliances']:
appliances.append(IPAppliance(hostname=appliance['ip_address']))
return appliances, request_id
[docs] def destroy_pool(self, pool_id):
self.call_method('destroy_pool', id=pool_id)