adding python helix admin library
Project: http://git-wip-us.apache.org/repos/asf/helix/repo Commit: http://git-wip-us.apache.org/repos/asf/helix/commit/e2a033f8 Tree: http://git-wip-us.apache.org/repos/asf/helix/tree/e2a033f8 Diff: http://git-wip-us.apache.org/repos/asf/helix/diff/e2a033f8 Branch: refs/heads/master Commit: e2a033f8563538a4cae1e73a3a7f95a302751a98 Parents: af77829 Author: Jon Heise <[email protected]> Authored: Thu Dec 4 15:38:20 2014 -0800 Committer: Jon Heise <[email protected]> Committed: Thu Dec 4 15:38:20 2014 -0800 ---------------------------------------------------------------------- contributors/py-helix-admin/helix/__init__.py | 0 contributors/py-helix-admin/helix/cluster.py | 346 ++++++++++++ contributors/py-helix-admin/helix/functions.py | 543 +++++++++++++++++++ .../py-helix-admin/helix/helixexceptions.py | 16 + .../py-helix-admin/helix/participant.py | 66 +++ contributors/py-helix-admin/helix/partition.py | 18 + .../py-helix-admin/helix/resourcegroup.py | 34 ++ 7 files changed, 1023 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/__init__.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/__init__.py b/contributors/py-helix-admin/helix/__init__.py new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/cluster.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/cluster.py b/contributors/py-helix-admin/helix/cluster.py new file mode 100644 index 0000000..f2b931e --- /dev/null +++ b/contributors/py-helix-admin/helix/cluster.py @@ -0,0 +1,346 @@ +"""base class for anything that connects to helix""" + +from participant import Participant +from partition import Partition +from resourcegroup import ResourceGroup + +from helixexceptions import HelixException +import functions + + +class Cluster(object): + """Basic model of a cluster, holds participants, partitions, slices, + external view, ideal state, resource groups""" + ver = (1, 0) + + def __init__(self, host, cluster): + super(Cluster, self).__init__() + self.host = host + self.cluster = cluster + + # dynamically loaded data below + self._partitions = {} + self._participants = {} + self._resources = {} + self._ideal_state = {} + self._external_view = {} + + def __str__(self): + return "{0} Object for {1}".format(self.__class__.__name__, + self.cluster) + + def __repr__(self): + return "{0}({1}, {2})".format(self.__class__.__name__, self.cluster, + self.host) + + def load_resources(self): + """queries helix for resource groups and loades them into model""" + try: + for cur_resource in functions.get_resource_groups(self.host, + self.cluster): + data = functions.get_resource_group(self.host, self.cluster, + cur_resource) + name = data["id"] + count = data["simpleFields"]["NUM_PARTITIONS"] + replicas = data["simpleFields"]["REPLICAS"] + statemode = data["simpleFields"]["STATE_MODEL_DEF_REF"] + resource = ResourceGroup(name, + count, replicas, + statemode, data) + partitions = data["mapFields"] + for part, hosts in partitions.items(): + phosts = [] + for host, status in hosts.items(): + participant = self.participants[host] + participant.partitions[part] = status + phosts.append(participant) + + partition = Partition(part, phosts) + resource.add_partition(partition) + + self._resources[cur_resource] = resource + except HelixException: + pass + + @property + def resources(self): + """sanely handle resource loading and usage""" + if not self._resources: + self.load_resources() + return self._resources + + @resources.setter + def resources(self, value): + """ensure an exception is raise on an attempt to set resource groups""" + raise HelixException("Resource groups cannont be added in this manner") + + def _cluster_exists(self): + """verify cluster exists in helix""" + if self.cluster in functions.get_clusters(self.host): + return True + return False + + def load_participants(self): + """create instances of storage node for participants in this cluster""" + self._participants = {} + + try: + instances = functions.get_instances(self.host, self.cluster) + for instance in instances: + ident = instance["id"] + enabled = instance["simpleFields"]["HELIX_ENABLED"] + alive = instance["simpleFields"]["Alive"] + data = instance + participant = Participant(ident, alive, enabled, data) + self._participants[instance["id"]] = participant + except HelixException: + pass + + @property + def participants(self): + """returns participants, if not loaded, loads them then returns""" + if not self._participants: + self.load_participants() + return self._participants + + @participants.setter + def participants(self, value): + raise HelixException("Participants cannot added in this fashion!") + + def load_partitions(self): + """query partitions from helix and load into model""" + self._partitions = {} + for resource in self.resources: + newstate = functions.get_ideal_state(self.host, self.cluster, + resource) + self._partitions[resource] = {} + if newstate: + for part in newstate: + hosts = [self.participants[x] for x in newstate[part]] + partition = Partition(part, hosts) + self._partitions[resource][part] = partition + for host in newstate[part]: + self.participants[host].partitions[part] = partition + + @property + def partitions(self): + """return partitions""" + if not self._partitions: + self.load_partitions() + return self._partitions + + def load_ideal_state(self): + """query ideal state from helix and load into model""" + self._ideal_state = {} + for resource in self.resources: + self._ideal_state[resource] = \ + functions.get_ideal_state(self.host, self.cluster, resource) + + @property + def ideal_state(self): + """return ideal state""" + if not self._ideal_state: + self.load_ideal_state() + return self._ideal_state + + @ideal_state.setter + def ideal_state(self, value): + """setter for ideal state""" + raise HelixException("Cannot adjust Ideal State in this manner") + + def load_external_view(self): + """query external view from helix and load into model""" + self._external_view = {} + for resource in self.resources: + self._external_view[resource] = \ + functions.get_external_view(self.host, self.cluster, resource) + + @property + def external_view(self): + """return external view""" + if not self._external_view: + self.load_external_view() + return self._external_view + + @external_view.setter + def external_view(self, value): + """setter for external view""" + raise HelixException("External View cannot be modified!") + + def get_config(self, config): + """ get requested config from helix""" + return functions.get_config(self.host, self.cluster, config) + + def set_cluster_config(self, config): + """ set given configs in helix""" + return functions.set_config(self.host, self.cluster, config) + + def set_resource_config(self, config, resource): + """ set given configs in helix""" + rname = resource + if isinstance(resource, ResourceGroup): + rname = resource.name + return functions.set_config(self.host, self.cluster, config, + resource=rname) + + def set_participant_config(self, config, participant): + pname = participant + if isinstance(participant, Participant): + pname = participant.ident + """ set given configs in helix""" + return functions.set_config(self.host, self.cluster, config, + participant=pname) + + def activate_cluster(self, grand, enabled=True): + """activate this cluster with the specified grand cluster""" + return functions.activate_cluster(self.host, self.cluster, grand, + enabled) + + def deactivate_cluster(self, grand): + """deactivate this cluster against the given grandcluster""" + return functions.deactivate_cluster(self.host, self.cluster, grand) + + def add_cluster(self): + """add cluster to helix""" + return functions.add_cluster(self.host, self.cluster) + + def add_instance(self, instances, port): + """add instance to cluster""" + return functions.add_instance(self.host, self.cluster, instances, port) + + def rebalance(self, resource, replicas, key=""): + """rebalance a resource group""" + return functions.rebalance(self.host, self.cluster, resource, + replicas, key) + + def add_resource(self, resource, partitions, state_model_def, mode=""): + """add resource to cluster""" + return functions.add_resource(self.host, self.cluster, resource, + partitions, state_model_def, mode) + + def enable_instance(self, instance, enabled=True): + """enable instance, assumes instance a participant object""" + ident = None + if isinstance(instance, Participant): + ident = instance.ident + elif isinstance(instance, str): + ident = instance + else: + raise HelixException("Instance must be a string or participant") + return functions.enable_instance(self.host, self.cluster, ident, + enabled) + + def disable_instance(self, instance): + """disable instance, assumes instance is a participant object""" + return self.enable_instance(instance, enabled=False) + + def enable_partition(self, resource, partition, instance, enabled=True): + """enable partition, assumes instance and partition are + helix objects""" + ident = None + part_id = None + + if isinstance(instance, Participant): + ident = instance.ident + elif isinstance(instance, str): + ident = instance + else: + raise HelixException("Instance must be a string or participant") + + if isinstance(partition, Partition): + part_id = partition.name + elif isinstance(partition, str): + part_id = partition + else: + raise HelixException("Partition must be a string or partition") + + return functions.enable_partition(self.host, self.cluster, resource, + part_id, ident, enabled) + + def disable_partition(self, resource, partition, instance): + """disable partition, conveience function for enable partition""" + return self.enable_partition(resource, partition, instance, + enabled=False) + + def enable_resource(self, resource, enabled=True): + """enable/disable resource""" + resource_name = None + if isinstance(resource, ResourceGroup): + resource_name = resource.name + elif isinstance(resource, str): + resource_name = resource + else: + raise HelixException( + "Resource must be a string or a resource group object") + + return functions.enable_resource(self.host, self.cluster, + resource_name, enabled) + + def disable_resource(self, resource): + """disable given function""" + return self.enable_resource(resource, enabled=False) + + def add_resource_tag(self, resource, tag): + """add a tag to a resource""" + resource_name = None + if isinstance(resource, ResourceGroup): + resource_name = resource.name + elif isinstance(resource, str): + resource_name = resource + else: + raise HelixException("Resource must be resource object or string") + + return functions.add_resource_tag(self.host, self.cluster, + resource_name, tag) + + # del resource not yet available in api + # def del_resource_tag(self, resource, tag): + # """del a tag to a resource""" + # resource_name = None + # if isinstance(resource, ResourceGroup): + # resource_name = resource.name + # elif isinstance(resource, str): + # resource_name = resource + # else: + # raise HelixException("Resource must be resource object or str") + # + # return functions.del_resource_tag(self.host, self.cluster, + # resource_name, tag) + + def add_instance_tag(self, instance, tag): + ident = None + + if isinstance(instance, Participant): + ident = instance.ident + elif isinstance(instance, str): + ident = instance + else: + raise HelixException("Instance must be a string or participant") + + return functions.add_instance_tag(self.host, self.cluster, ident, tag) + + def del_instance_tag(self, instance, tag): + ident = None + + if isinstance(instance, Participant): + ident = instance.ident + elif isinstance(instance, str): + ident = instance + else: + raise HelixException("Instance must be a string or participant") + + return functions.del_instance_tag(self.host, self.cluster, ident, tag) + + def del_instance(self, instance): + """remove instance from cluster, assumes instance is a + participant object""" + return functions.del_instance(self.host, self.cluster, instance.ident) + + def del_resource(self, resource): + """remove resource group from cluster, assumes resource is a + resource object""" + return functions.del_resource(self.host, self.cluster, resource.name) + + def del_cluster(self): + """remove cluster from helix""" + return functions.del_cluster(self.host, self.cluster) http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/functions.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/functions.py b/contributors/py-helix-admin/helix/functions.py new file mode 100644 index 0000000..b66cca7 --- /dev/null +++ b/contributors/py-helix-admin/helix/functions.py @@ -0,0 +1,543 @@ +"""library to handle helix commands""" +import json +from restkit import Resource + +from helixexceptions import HelixException +from helixexceptions import HelixAlreadyExistsException +from helixexceptions import HelixDoesNotExistException + + +def _post_payload(host, path, data, **kwargs): + """generic function to handle posting data + :rtype : return body of page + :param host: host to send data to + :param path: path to interact with + :param data: data to send + :param kwargs: additional keyword args + """ + + if "http://" not in host: + host = "http://{0}".format(host) + + res = Resource(host) + + payload = "jsonParameters={0}".format(json.dumps(data)) + for key, value in kwargs.items(): + payload += '&{0}={1}'.format(key, json.dumps(value)) + headers = {"Content-Type": "application/json"} + # print "path is %s" % path + page = res.post(path=path, payload=payload, headers=headers) + body = page.body_string() + if body: + body = json.loads(body) + + if isinstance(body, dict) and "ERROR" in body: + raise HelixException(body["ERROR"]) + + # test what was returned, see if any exceptions need to be raise + # if not body: + # raise HelixException("body for path {0} is empty".format(path)) + # else: + # print "BODY IS EMPTY FOR ", path + # print "BODY is %s." % body + + return body + + +def _get_page(host, path): + """if we're specifying a cluster then verify that a cluster is set""" + + if "http://" not in host: + host = "http://{0}".format(host) + + res = Resource(host) + + page = res.get(path=path) + data = page.body_string() + body = None + try: + body = json.loads(data) + except ValueError: + body = json.loads(data[:-3]) + + # test what was returned, see if any exceptions need to be raise + if not body: + raise HelixException("body for path {0} is empty".format(path)) + + if isinstance(body, dict) and "ERROR" in body: + raise HelixException(body["ERROR"]) + + return body + + +def _delete_page(host, path): + """delete page at a given path""" + retval = None + if "http://" not in host: + host = "http://{0}".format(host) + + res = Resource(host) + + page = res.delete(path) + data = page.body_string() + if data: + retval = json.loads(data) + + return retval + + +def get_clusters(host): + """ querys helix cluster for all clusters """ + return _get_page(host, "/clusters")["listFields"]["clusters"] + + +def get_resource_groups(host, cluster): + """ querys helix cluster for resources groups of the current cluster""" + return _get_page(host, "/clusters/{0}/resourceGroups".format(cluster))[ + "listFields"]["ResourceGroups"] + + +def get_resource_tags(host, cluster): + """returns a dict of resource tags for a cluster""" + return _get_page(host, "/clusters/{0}/resourceGroups".format(cluster))[ + "mapFields"]["ResourceTags"] + + +def get_resource_group(host, cluster, resource): + """ gets the ideal state of the specified resource group of the + current cluster""" + if resource not in get_resource_groups(host, cluster): + raise HelixException( + "{0} is not a resource group of {1}".format(resource, cluster)) + + return _get_page(host, "/clusters/{0}/resourceGroups/{1}".format(cluster, + resource)) + + +def get_ideal_state(host, cluster, resource): + """ gets the ideal state of the specified resource group of the + current cluster""" + + if resource not in get_resource_groups(host, cluster): + raise HelixException( + "{0} is not a resource group of {1}".format(resource, cluster)) + + return _get_page(host, "/clusters/{0}/resourceGroups/{1}/idealState". + format(cluster, resource))["mapFields"] + + +def get_external_view(host, cluster, resource): + """return the external view for a given cluster and resource""" + if resource not in get_resource_groups(host, cluster): + raise HelixException( + "{0} is not a resource group of {1}".format(resource, cluster)) + + return _get_page(host, + "/clusters/{0}/resourceGroups/{1}/externalView".format( + cluster, resource))["mapFields"] + + +def get_instances(host, cluster): + """get list of instances registered to the cluster""" + if not cluster: + raise HelixException("Cluster must be set before " + "calling this function") + + return _get_page(host, "/clusters/{0}/instances".format(cluster))[ + "instanceInfo"] + + +def get_instance_detail(host, cluster, name): + """get details of an instance""" + return _get_page(host, "/clusters/{0}/instances/{1}".format(cluster, name)) + + +def get_config(host, cluster, config): + """get requested config""" + return _get_page(host, "/clusters/{0}/configs/{1}".format(cluster, config)) + + +def add_cluster(host, cluster): + """add a cluster to helix""" + if cluster in get_clusters(host): + raise HelixAlreadyExistsException( + "Cluster {0} already exists".format(cluster)) + + data = {"command": "addCluster", + "clusterName": cluster} + + page = _post_payload(host, "/clusters", data) + return page + + +def add_instance(host, cluster, instances, port): + """add a list of instances to a cluster""" + if cluster not in get_clusters(host): + raise HelixDoesNotExistException( + "Cluster {0} does not exist".format(cluster)) + + if not isinstance(instances, list): + instances = [instances] + instances = ["{0}:{1}".format(instance, port) for instance in instances] + try: + newinstances = set(instances) + oldinstances = set( + [x["id"].replace('_', ':') for x in get_instances(host, cluster)]) + instances = list(newinstances - oldinstances) + except HelixException: + # this will get thrown if instances is empty, + # which if we're just populating should happen + pass + + if instances: + data = {"command": "addInstance", + "instanceNames": ";".join(instances)} + + instance_path = "/clusters/{0}/instances".format(cluster) + # print "adding to", instance_path + page = _post_payload(host, instance_path, data) + return page + + else: + raise HelixAlreadyExistsException( + "All instances given already exist in cluster") + + +def rebalance(host, cluster, resource, replicas, key=""): + """rebalance the given resource group""" + if resource not in get_resource_groups(host, cluster): + raise HelixException( + "{0} is not a resource group of {1}".format(resource, cluster)) + + data = {"command": "rebalance", + "replicas": replicas} + + if key: + data["key"] = key + page = _post_payload(host, + "/clusters/{0}/resourceGroups/{1}/idealState".format( + cluster, resource), data) + return page + + +def activate_cluster(host, cluster, grand_cluster, enabled=True): + """activate the cluster with the grand cluster""" + if grand_cluster not in get_clusters(host): + raise HelixException( + "grand cluster {0} does not exist".format(grand_cluster)) + + data = {'command': 'activateCluster', + 'grandCluster': grand_cluster} + + if enabled: + data["enabled"] = "true" + else: + data["enabled"] = "false" + + page = _post_payload(host, "/clusters/{0}".format(cluster), data) + return page + + +def deactivate_cluster(host, cluster, grand_cluster): + """deactivate the cluster with the grand cluster""" + return activate_cluster(host, cluster, grand_cluster, enabled=False) + + +def add_resource(host, cluster, resource, partitions, + state_model_def, mode=""): + """Add given resource group""" + if resource in get_resource_groups(host, cluster): + raise HelixAlreadyExistsException( + "ResourceGroup {0} already exists".format(resource)) + + data = {"command": "addResource", + "resourceGroupName": resource, + "partitions": partitions, + "stateModelDefRef": state_model_def} + + if mode: + data["mode"] = mode + + return _post_payload(host, "/clusters/{0}/resourceGroups".format(cluster), + data) + + +def enable_resource(host, cluster, resource, enabled=True): + """enable or disable specified resource""" + data = {"command": "enableResource"} + if enabled: + data["enabled"] = "true" + else: + data["enabled"] = "false" + + return _post_payload(host, "/clusters/{0}/resourceGroups/{1}".format( + cluster, resource), data) + + +def disable_resource(host, cluster, resource): + """function for disabling resources""" + return enable_resource(host, cluster, resource, enabled=False) + + +def alter_ideal_state(host, cluster, resource, newstate): + """alter ideal state""" + data = {"command": "alterIdealState"} + return _post_payload(host, + "/clusters/{0}/resourceGroups/{1}/idealState".format( + cluster, resource), data, + newIdealState=newstate) + + +def enable_instance(host, cluster, instance, enabled=True): + """enable instance within cluster""" + data = {"command": "enableInstance"} + if enabled: + data["enabled"] = "true" + else: + data["enabled"] = "false" + + return _post_payload(host, "/clusters/{0}/instances/{1}".format(cluster, + instance), + data) + + +def disable_instance(host, cluster, instance): + """wrapper for ease of use for disabling an instance""" + return enable_instance(host, cluster, instance, enabled=False) + + +def swap_instance(host, cluster, old, new): + """swap instance""" + data = {"command": "swapInstance", + "oldInstance": old, + "newInstance": new} + + return _post_payload(host, "/cluster/{0}/instances".format(cluster), data) + + +def enable_partition(host, cluster, resource, partition, instance, + enabled=True): + """enable Partition """ + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + data = {"command": "enablePartition", + "resource": resource, + "partition": partition, + "enabled": enabled} + return _post_payload(host, "/clusters/{0}/instances/{1}".format(cluster, + instance), + data) + + +def disable_partition(host, cluster, resource, partitions, instance): + """disable Partition """ + return enable_partition(host, cluster, resource, partitions, instance, + enabled=False) + + +def reset_partition(host, cluster, resource, partitions, instance): + """reset partition""" + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + data = {"command": "resetPartition", + "resource": resource, + "partition": " ".join(partitions)} + return _post_payload(host, "/clusters/{0}/instances/{1}".format(cluster, + instance), + data) + + +def reset_resource(host, cluster, resource): + """reset resource""" + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + data = {"command": "resetResource"} + return _post_payload(host, + "/clusters/{0}/resourceGroups/{1}".format(cluster, + resource), + data) + + +def reset_instance(host, cluster, instance): + """reset instance""" + if instance not in get_instances(host, cluster): + raise HelixDoesNotExistException( + "Instance {0} does not exist".format(instance)) + + data = {"command": "resetInstance"} + return _post_payload(host, "/clusters/{0}/instances/{1}".format(cluster, + instance), + data) + + +def add_instance_tag(host, cluster, instance, tag): + """add tag to an instance""" + data = {"command": "addInstanceTag", + "instanceGroupTag": tag} + return _post_payload(host, + "/clusters/{0}/instances/{1}".format( + cluster, instance), data) + + +def del_instance_tag(host, cluster, instance, tag): + """remove tag from instance""" + data = {"command": "removeInstanceTag", + "instanceGroupTag": tag} + return _post_payload(host, + "/clusters/{0}/instances/{1}".format( + cluster, instance), data) + + +def add_resource_tag(host, cluster, resource, tag): + """add tag to resource group""" + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + data = {"command": "addResourceProperty", + "INSTANCE_GROUP_TAG": tag} + return _post_payload(host, + "/clusters/{0}/resourceGroups/{1}/idealState".format( + cluster, resource), data) + + +""" +del resource currently does not exist in helix api +def del_resource_tag(host, cluster, resource, tag): + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + data = {"command": "removeResourceProperty", + "INSTANCE_GROUP_TAG": tag} + return _post_payload(host, + "/clusters/{0}/resourceGroups/{1}/idealState".format( + cluster, resource), data) +""" + + +def get_instance_taginfo(host, cluster): + return _get_page(host, "/clusters/{0}/instances".format( + cluster))["tagInfo"] + + +def expand_cluster(host, cluster): + """expand cluster""" + data = {"command": "expandCluster"} + + return _post_payload(host, "/clusters/{0}/".format(cluster), data) + + +def expand_resource(host, cluster, resource): + """expand resource""" + data = {"command": "expandResource"} + + return _post_payload(host, + "/clusters/{0}/resourceGroup/{1}/idealState".format( + cluster, resource), data) + + +def add_resource_property(host, cluster, resource, properties): + """add resource property properties must be a dictionary of properties""" + properties["command"] = "addResourceProperty" + + return _post_payload(host, + "/clusters/{0}/resourceGroup/{1}/idealState".format( + cluster, resource), properties) + + +def _handle_config(host, cluster, configs, command, participant=None, + resource=None): + """helper function to set or delete configs in helix""" + data = {"command": "{0}Config".format(command), + "configs": ",".join( + ["{0}={1}".format(x, y) for x, y in configs.items()])} + + address = "/clusters/{0}/configs/".format(cluster) + if participant: + address += "participant/{0}".format(participant) + elif resource: + address += "resource/{0}".format(resource) + else: + address += "cluster" + + return _post_payload(host, address, data) + + +def set_config(host, cluster, configs, participant=None, resource=None): + """sets config in helix""" + return _handle_config(host, cluster, configs, "set", participant, resource) + + +def remove_config(host, cluster, configs, participant=None, resource=None): + """sets config in helix""" + return _handle_config(host, "remove", cluster, configs, participant, + resource) + + +def get_zk_path(host, path): + """get zookeeper path""" + return _get_page(host, "zkPath/{0}".format(path)) + + +def del_zk_path(host, path): + """delete zookeeper path""" + return _delete_page(host, "zkPath/{0}".format(path)) + + +def get_zk_child(host, path): + """get zookeeper child""" + return _get_page(host, "zkChild/{0}".format(path)) + + +def del_zk_child(host, path): + """delete zookeeper child""" + return _delete_page(host, "zkChild/{0}".format(path)) + + +def add_state_model(host, cluster, newstate): + """add state model""" + data = {"command": "addStateModel"} + + return _post_payload(host, "/clusters/{0}/StateModelDefs".format(cluster), + data, newStateModelDef=newstate) + + +def del_instance(host, cluster, instance): + """delete instance""" + if instance not in [x["id"] for x in get_instances(host, cluster)]: + raise HelixDoesNotExistException( + "Instance {0} does not exist.".format(instance)) + + page = _delete_page(host, + "/clusters/{0}/instances/{1}".format(cluster, + instance)) + return page + + +def del_resource(host, cluster, resource): + """delete specified resource from cluster""" + if resource not in get_resource_groups(host, cluster): + raise HelixDoesNotExistException( + "ResourceGroup {0} does not exist".format(resource)) + + page = _delete_page(host, "/clusters/{0}/resourceGroups/{1}".format( + cluster, resource)) + return page + + +def del_cluster(host, cluster): + """delete cluster""" + page = _delete_page(host, "/clusters/{0}".format(cluster)) + + return page + + +def send_message(host, cluster, path, **kwargs): + pass http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/helixexceptions.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/helixexceptions.py b/contributors/py-helix-admin/helix/helixexceptions.py new file mode 100644 index 0000000..63ffec1 --- /dev/null +++ b/contributors/py-helix-admin/helix/helixexceptions.py @@ -0,0 +1,16 @@ +"""library to handle helix exceptions""" + + +class HelixException(Exception): + """Base helix exception""" + pass + + +class HelixAlreadyExistsException(HelixException): + """Exception is thrown when an entry in helix already exists""" + pass + + +class HelixDoesNotExistException(HelixException): + """Exception is thrown when an entry in helix does not exist""" + pass http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/participant.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/participant.py b/contributors/py-helix-admin/helix/participant.py new file mode 100644 index 0000000..678f93b --- /dev/null +++ b/contributors/py-helix-admin/helix/participant.py @@ -0,0 +1,66 @@ +"""base class for anything that connects to helix""" +from helixexceptions import HelixException + + +class Participant(object): + """Basic model for a helix participant""" + + def __init__(self, ident, alive, enabled, data): + super(Participant, self).__init__() + self.ident = ident + self.hostname, self.port = ident.split("_") + self.partitions = {} + self.data = data + self.enabled = None + self._tags = [] + self._disabled_partitions = [] + + if isinstance(enabled, str) or isinstance(enabled, unicode): + if enabled == "true": + self.enabled = True + else: + self.enabled = False + elif isinstance(enabled, bool): + self.enabled = enabled + + self.alive = bool(alive) + self.update() + + def __repr__(self): + return "{0}('{1}', {2}, {3}, {4})".format(self.__class__.__name__, + self.ident, self.alive, + self.enabled, self.data) + + def __str__(self): + return "Id: {0} Enabled: {1} Alive: {2}".format(self.ident, + self.enabled, + self.alive) + + def update(self, data=None): + """update data for participant then update values""" + if data: + self.data = data + + if "TAG_LIST" in self.data["listFields"]: + self._tags = self.data["listFields"]["TAG_LIST"] + + if "HELIX_DISABLED_PARTITION" in self.data["listFields"]: + self._disabled_partitions = \ + self.data["listFields"]["HELIX_DISABLED_PARTITION"] + + @property + def tags(self): + return self._tags + + @tags.setter + def tags(self, value): + """ensure an exception is raise on an attempt to set tags this way""" + raise HelixException("Tags must be set on a cluster object") + + @property + def disabled_partitions(self): + return self._disabled_partitions + + @disabled_partitions.setter + def disabled_partitions(self, value): + raise HelixException("Partitions must be disabled on a cluster object") http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/partition.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/partition.py b/contributors/py-helix-admin/helix/partition.py new file mode 100644 index 0000000..e028567 --- /dev/null +++ b/contributors/py-helix-admin/helix/partition.py @@ -0,0 +1,18 @@ +"""base class for anything that connects to helix""" + + +class Partition(object): + """Object to deal helix partitions""" + + def __init__(self, name, hosts): + super(Partition, self).__init__() + self.name = name + self.hosts = hosts + + def __str__(self): + return "Partition {0} - Hosts: {1}".format(self.name, ", ".join( + [x.ident for x in self.hosts])) + + def __repr__(self): + return "{0}('{1}', {2})".format(self.__class__.__name__, self.name, + self.hosts) http://git-wip-us.apache.org/repos/asf/helix/blob/e2a033f8/contributors/py-helix-admin/helix/resourcegroup.py ---------------------------------------------------------------------- diff --git a/contributors/py-helix-admin/helix/resourcegroup.py b/contributors/py-helix-admin/helix/resourcegroup.py new file mode 100644 index 0000000..62c447f --- /dev/null +++ b/contributors/py-helix-admin/helix/resourcegroup.py @@ -0,0 +1,34 @@ +"""base class for anything that connects to helix""" + +import partition +from helixexceptions import HelixException + + +class ResourceGroup(object): + """Object to deal with resource groups""" + + def __init__(self, name, count, replicas, statemode, data): + super(ResourceGroup, self).__init__() + self.name = name + self.count = count + self.replicas = replicas + self.state_model_def_ref = statemode + self.data = data + self.partitions = {} + + def __str__(self): + return "Resource: {0} - Count: {1}".format(self.name, self.count) + + def __repr__(self): + return "{0}('{1}', {2}, {3}, {4}, {5})".format(self.__class__.__name__, + self.name, self.count, + self.replicas, + self. + state_model_def_ref, + self.data) + + def add_partition(self, part): + """add a partition to this resource group""" + if not isinstance(part, partition.Partition): + raise HelixException("Argument part must be Partition or subclass") + self.partitions[part.name] = part
