diff --git a/senlinclient/v1/client.py b/senlinclient/v1/client.py index ee429215..ce9639ed 100644 --- a/senlinclient/v1/client.py +++ b/senlinclient/v1/client.py @@ -10,39 +10,667 @@ # License for the specific language governing permissions and limitations # under the License. +import six +from six.moves.urllib import parse + from senlinclient.common import http -from senlinclient.v1 import actions -from senlinclient.v1 import build_info -from senlinclient.v1 import clusters -from senlinclient.v1 import events -from senlinclient.v1 import nodes -from senlinclient.v1 import policies -from senlinclient.v1 import policy_types -from senlinclient.v1 import profile_types -from senlinclient.v1 import profiles +from senlinclient.openstack.common.apiclient import base + + +class Action(base.Resource): + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def delete(self): + return self.manager.delete(self) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class ActionManager(base.BaseManager): + resource_class = Action + + def list(self, **kwargs): + """Get a list of actions. + :param limit: maximum number of actions to return + :param marker: begin returning actions that appear later in the + list than that represented by this action id + :param filters: dict of direct comparison filters that mimics the + structure of a action object + :rtype: list of :class:`Action` + """ + def paginate(params): + '''Paginate actions, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/actions?%s' % parse.urlencode(params, True) + actions = self._list(url, 'actions') + for action in actions: + yield action + + count = len(actions) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = action.id + for action in paginate(params): + yield action + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def create(self, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/actions', + data=kwargs, headers=headers) + return body + + def cancel(self, action_id): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/actions/%s/cancel' % action_id, + headers=headers) + return body + + def delete(self, action_id): + self._delete('/actions/%s' % action_id) + + +class BuildInfo(base.Resource): + def __repr__(self): + return "" % self._info + + def build_info(self): + return self.manager.build_info() + + +class BuildInfoManager(base.BaseManager): + resource_class = BuildInfo + + def build_info(self): + resp, body = self.client.json_request('GET', '/build_info') + return body + + +class Cluster(base.Resource): + def __repr__(self): + return "" % self._info + + def create(self, **fields): + return self.manager.create(self.identifier, **fields) + + def update(self, **fields): + self.manager.update(self.identifier, **fields) + + def delete(self): + return self.manager.delete(self.identifier) + + def get(self): + # set_loaded() first ... so if we have to bail, we know we tried. + self._loaded = True + if not hasattr(self.manager, 'get'): + return + + new = self.manager.get(self.identifier) + if new: + self._add_details(new._info) + + @property + def identifier(self): + return '%s/%s' % (self.name, self.id) + + +class ClusterManager(base.BaseManager): + resource_class = Cluster + + def list(self, **kwargs): + """Get a list of clusters. + :param limit: maximum number of clusters to return + :param marker: begin returning clusters that appear later in the + cluster list than that represented by this cluster id + :param filters: dict of direct comparison filters that mimics the + structure of a cluster object + :rtype: list of :class:`Cluster` + """ + def paginate(params): + '''Paginate clusters, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/clusters?%s' % parse.urlencode(params, True) + clusters = self._list(url, 'clusters') + for cluster in clusters: + yield cluster + + count = len(clusters) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = cluster.id + for cluster in paginate(params): + yield cluster + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def create(self, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/clusters', + data=kwargs, headers=headers) + return body + + def update(self, cluster_id, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'PUT', + '/clusters/%s' % cluster_id, + data=kwargs, headers=headers) + + def delete(self, cluster_id): + """Delete a cluster.""" + self._delete("/clusters/%s" % cluster_id) + + def add_node(self, cluster_id, node_id): + headers = self.client.credentials_headers() + cluster = self.get(cluster_id) + data = {'node_id': node_id} + resp, body = self.client.json_request( + 'POST', + '/clusters/%s/nodes' % cluster.identifier, + data=data, headers=headers) + + def del_node(self, cluster_id, node_id): + cluster = self.get(cluster_id) + resp, body = self.client.json_request( + 'DELETE', + '/clusters/%s/nodes/%s' % (cluster.identifier, node_id)) + + def list_policy(self, cluster_id): + cluster = self.get(cluster_id) + resp, body = self.client.json_request( + 'GET', + '/clusters/%s/policies' % cluster.identifier) + return body + + def attach_policy(self, cluster_id, policy_id): + """Attach a policy to a cluster.""" + cluster = self.get(cluster_id) + data = {'policy_id': policy_id} + resp, body = self.client.json_request( + 'POST', + '/clusters/%s/policies' % cluster.identifier, + data=data) + + def detach_policy(self, cluster_id, policy_id): + cluster = self.get(cluster_id) + resp, body = self.client.json_request( + 'DELETE', + '/clusters/%s/policies/%s' % (cluster.identifier, policy_id)) + + def update_policy(self, cluster_id, **kwargs): + cluster = self.get(cluster_id) + policy_id = kwargs.pop('policy_id') + resp, body = self.client.json_request( + 'POST', + '/clusters/%s/policies/%s' % (cluster.identifier, policy_id), + data=kwargs) + + def show_policy(self, cluster_id, policy_id): + cluster = self.get(cluster_id) + resp, body = self.client.json_request( + 'GET', + '/clusters/%s/policies/%s' % (cluster.identifier, policy_id)) + return body + + def get(self, cluster_id): + resp, body = self.client.json_request( + 'GET', + '/clusters/%s' % cluster_id) + return Cluster(self, body['cluster']) + + +class Event(base.Resource): + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def delete(self): + return self.manager.delete(self) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class EventManager(base.BaseManager): + resource_class = Event + + def list(self, **kwargs): + """Get a list of events. + :param limit: maximum number of events to return + :param marker: begin returning events that appear later in the + list than that represented by this event id + :param filters: dict of direct comparison filters that mimics the + structure of a event object + :rtype: list of :class:`Event` + """ + def paginate(params): + '''Paginate events, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/events?%s' % parse.urlencode(params, True) + events = self._list(url, 'events') + for event in events: + yield event + + count = len(events) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = event.id + for event in paginate(params): + yield event + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def delete(self, event_id): + self._delete("/events/%s" % event_id) + + def get(self, event_id): + '''Get the details for a specific event.''' + resp, body = self.client.json_request( + 'GET', + '/events/%s' % event_id) + return Event(self, body['event']) + + +class Node(base.Resource): + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def delete(self): + return self.manager.delete(self) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class NodeManager(base.BaseManager): + resource_class = Node + + def list(self, **kwargs): + """Get a list of nodes. + :param limit: maximum number of nodes to return + :param marker: begin returning nodes that appear later in the + list than that represented by this node id + :param filters: dict of direct comparison filters that mimics the + structure of a node object + :rtype: list of :class:`Node` + """ + def paginate(params): + '''Paginate nodes, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/nodes?%s' % parse.urlencode(params, True) + nodes = self._list(url, 'nodes') + for node in nodes: + yield node + + count = len(nodes) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = node.id + for node in paginate(params): + yield node + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def create(self, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/nodes', data=kwargs, headers=headers) + return body + + def delete(self, node_id): + self._delete('/nodes/%s' % node_id) + + def update(self, node_id, kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'PATCH', + '/nodes/%s' % node_id, + data=kwargs, headers=headers) + + def get(self, node_id): + '''Get the details for a specific node.''' + resp, body = self.client.json_request( + 'GET', + '/nodes/%s' % node_id) + return Node(self, body['node']) + + def profile(self, node_id): + '''Get the profile spec for a specific node as a parsed JSON.''' + resp, body = self.client.json_request( + 'GET', + '/nodes/%s/profile' % node_id) + return body + + +class PolicyType(base.Resource): + def __repr__(self): + return "" % self._info + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + def _add_details(self, info): + self.policy_type = info + + +class PolicyTypeManager(base.BaseManager): + resource_class = PolicyType + + def list(self): + """Get a list of policy types. + :rtype: list of :class:`PolicyType` + """ + return self._list('/policy_types', 'policy_types') + + def get(self, policy_type): + '''Get the details for a specific policy_type.''' + url_str = parse.quote(encodeutils.safe_encode(policy_type), '') + resp, body = self.client.json_request( + 'GET', + '/policy_types/%s' % url_str) + return body + + def generate_template(self, policy_type): + url_str = parse.quote(encodeutils.safe_encode(policy_type), '') + resp, body = self.client.json_request( + 'GET', + '/policy_types/%s/template' % url_str) + return body + + +class Policy(base.Resource): + def __repr__(self): + return "" % self._info + + def create(self, **fields): + return self.manager.create(self.identifier, **fields) + + def update(self, **fields): + self.manager.update(self.identifier, **fields) + + def delete(self): + return self.manager.delete(self.identifier) + + def get(self): + # set _loaded() first ... so if we have to bail, we know we tried. + self._loaded = True + if not hasattr(self.manager, 'get'): + return + + new = self.manager.get(self.identifier) + if new: + self._add_details(new._info) + + @property + def identifier(self): + return '%s/%s' % (self.name, self.id) + + +class PolicyManager(base.BaseManager): + resource_class = Policy + + def list(self, **kwargs): + """Get a list of policies. + :param limit: maximum number of policies to return + :param marker: begin returning policies that appear later in the + list than that represented by this policy id + :param filters: dict of direct comparison filters that mimics the + structure of a policy object + :rtype: list of :class:`Policy` + """ + def paginate(params): + '''Paginate policies, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/policies?%s' % parse.urlencode(params, True) + policies = self._list(url, 'policies') + for policy in policies: + yield policy + + count = len(policies) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = policy.id + for policy in paginate(params): + yield policy + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def create(self, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/policies', + data=kwargs, headers=headers) + return body + + def update(self, policy_id, **kwargs): + '''We don't allow update to a policy.''' + return None + + def delete(self, policy_id): + """Delete a policy.""" + self._delete("/policies/%s" % policy_id) + + def get(self, policy_id): + resp, body = self.client.json_request( + 'GET', + '/policies/%s' % policy_id) + return Policy(self, body['policy']) + + +class ProfileType(base.Resource): + def __repr__(self): + return "" % self._info + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + def _add_details(self, info): + self.profile_type = info + + +class ProfileTypeManager(base.BaseManager): + resource_class = ProfileType + + def list(self): + """Get a list of profile types. + :rtype: list of :class:`ProfileType` + """ + return self._list('/profile_types', 'profile_types') + + def get(self, profile_type): + '''Get the details for a specific profile_type.''' + url_str = parse.quote(encodeutils.safe_encode(profile_type), '') + resp, body = self.client.json_request( + 'GET', + '/profile_types/%s' % url_str) + return body + + def generate_template(self, profile_type): + url_str = parse.quote(encodeutils.safe_encode(profile_type), '') + resp, body = self.client.json_request( + 'GET', + '/profile_types/%s/template' % url_str) + return body + + +class Profile(base.Resource): + def __repr__(self): + return "" % self._info + + def create(self, **fields): + return self.manager.create(self.identifier, **fields) + + def update(self, **fields): + self.manager.update(self.identifier, **fields) + + def delete(self): + return self.manager.delete(self.identifier) + + def get(self): + # set _loaded() first ... so if we have to bail, we know we tried. + self._loaded = True + if not hasattr(self.manager, 'get'): + return + + new = self.manager.get(self.identifier) + if new: + self._add_details(new._info) + + @property + def identifier(self): + return '%s/%s' % (self.name, self.id) + + +class ProfileManager(base.BaseManager): + resource_class = Profile + + def list(self, **kwargs): + """Get a list of profiles. + :param limit: maximum number of profiles to return + :param marker: begin returning profiles that appear later in the + list than that represented by this profile id + :param filters: dict of direct comparison filters that mimics the + structure of a profile object + :rtype: list of :class:`Profile` + """ + def paginate(params): + '''Paginate profiles, even if more than API limit.''' + current_limit = int(params.get('limit') or 0) + url = '/profiles?%s' % parse.urlencode(params, True) + profiles = self._list(url, 'profiles') + for profile in profiles: + yield profile + + count = len(profiles) + remaining = current_limit - count + if remaining > 0 and count > 0: + params['limit'] = remaining + params['marker'] = profile.id + for profile in paginate(params): + yield profile + + params = {} + if 'filters' in kwargs: + filters = kwargs.pop('filters') + params.update(filters) + + for key, value in six.iteritems(kwargs): + if value: + params[key] = value + + return paginate(params) + + def create(self, **kwargs): + headers = self.client.credentials_headers() + resp, body = self.client.json_request( + 'POST', + '/profiles', + data=kwargs, headers=headers) + return body + + def update(self, profile_id, **kwargs): + '''We don't allow update to a profile.''' + return None + + def delete(self, profile_id): + """Delete a profile.""" + self._delete("/profiles/%s" % profile_id) + + def get(self, profile_id): + resp, body = self.client.json_request( + 'GET', + '/profiles/%s' % profile_id) + return Profile(self, body['profile']) class Client(object): - """Client for the Senlin v1 API. + '''Client for the Senlin v1 API. - :param string endpoint: A user-supplied endpoint URL for the Senlin - service. - :param string token: Token for authentication. - :param integer timeout: Allows customization of the timeout for client - http requests. (optional) - """ + :param endpoint: A user-supplied endpoint URL for the Senlin service. + :param token: Token for authentication. + :param timeout: Allows customization of the timeout for client + HTTP requests. (optional) + ''' - def __init__(self, *args, **kwargs): + def __init__(self, endpoint, **kwargs): """Initialize a new client for the Senlin v1 API.""" - self.http_client = http._construct_http_client(*args, **kwargs) + self.hc = http.create_http_client(endpoint, **kwargs) - self.clusters = clusters.ClusterManager(self.http_client) - self.nodes = nodes.NodeManager(self.http_client) - self.profiles = profiles.ProfileManager(self.http_client) - self.policies = policies.PolicyManager(self.http_client) - self.policy_types = policy_types.PolicyTypeManager(self.http_client) - self.profile_types = profile_types.ProfileTypeManager(self.http_client) - self.events = events.EventManager(self.http_client) - self.actions = actions.ActionManager(self.http_client) - - self.build_info = build_info.BuildInfoManager(self.http_client) + self.actions = ActionManager(self.hc) + self.build_info = BuildInfoManager(self.hc) + self.clusters = ClusterManager(self.hc) + self.events = EventManager(self.hc) + self.nodes = NodeManager(self.hc) + self.profile_types = ProfileTypeManager(self.hc) + self.profiles = ProfileManager(self.hc) + self.policy_types = PolicyTypeManager(self.hc) + self.policies = PolicyManager(self.hc)