You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2014/05/31 09:21:13 UTC

[09/12] AMBARI-5482. Integrate Ambari Shell. (Janos Matyas and Krisztian Horvath via yusaku)

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/configuration.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/configuration.py b/ambari-client/python-client/src/main/python/ambari_client/model/configuration.py
new file mode 100755
index 0000000..883caf2
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/configuration.py
@@ -0,0 +1,138 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+
+from ambari_client.model.base_model import  BaseModel , ModelList
+from ambari_client.model import paths , status , utils
+
+
+
+
+def _get_configuration(resource_root, cluster_name , type , tag="version1"):
+  """
+  Get configuration of a cluster
+  @param resource_root: The root Resource .
+  @param cluster_name: cluster_name
+  @param type: type of config
+  @return: A ConfigModel object
+  """
+  dic = resource_root.get(paths.CONFIGURATION_PATH % (cluster_name, type, tag))
+  
+  if len(dic["items"]) == 0:
+    return None
+  
+  config_model = utils.ModelUtils.create_model(ConfigModel , dic["items"][0], resource_root, "NO_KEY")
+  ref_clss = utils.getREF_class_name("cluster_name")
+  config_model._setattr(ref_clss, dic["items"][0]['Config']['cluster_name'])
+  return config_model
+
+
+def _get_all_configuration(resource_root, cluster_name , type ):
+  """
+  Gets ALL configuration of a cluster of a given type
+  @param resource_root: The root Resource .
+  @param cluster_name: cluster_name
+  @param type: type of config
+  @return: A ConfigModel object
+  """
+  dic = resource_root.get(paths.CONFIGURATION_ALL_PATH % (cluster_name, type))
+  
+  if len(dic["items"]) == 0:
+    return None
+
+  objects = []
+  for cfgm in dic["items"]:
+      config_model = utils.ModelUtils.create_model(ConfigModel , cfgm, resource_root, "NO_KEY")
+      ref_clss = utils.getREF_class_name("cluster_name")
+      config_model._setattr(ref_clss, cfgm['Config']['cluster_name'])
+      objects.append(config_model)
+  return ModelList(objects)
+  
+  
+def _update_configuration(resource_root, cluster_name , type , tag , config_model):
+  """
+  Update configuration of a cluster
+  @param resource_root: The root Resource .
+  @param cluster_name: cluster_name
+  @param type: type of config
+  @param config_model: config model object
+  @return: A ConfigModel object
+  """
+  data = {"Clusters":{"desired_configs":{ "type":type, "tag":tag, "properties":config_model.properties}}}
+  resp = resource_root.put(path=paths.UPDATE_CONFIGURATION_PATH % cluster_name , payload=data )
+  return utils.ModelUtils.create_model(status.StatusModel, resp, resource_root, "NO_KEY")
+
+
+def _add_config(root_resource, cluster_name, type, tag , properties):
+  """
+  add configurations 
+  @param type: the type of config
+  @param tag: tag
+  @param properties: a dict of properties
+  @return: A StatusModel object
+  """
+  cpath = paths.CLUSTERS_CONFIG_PATH % cluster_name
+  data = {"Clusters":{"desired_configs":{"type":type, "tag":tag, "properties":properties}}}
+  resp = root_resource.put(path=cpath , payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY") 
+    
+
+def _create_config(root_resource, cluster_name, type, tag , properties):
+  """
+  create a new  configurations 
+  @param type: the type of config
+  @param tag: tag
+  @param properties: a dict of properties
+  @return: A StatusModel object
+  """
+  cpath = paths.CLUSTERS_CONFIG_PATH % cluster_name
+  data = {"type":type, "tag":tag, "properties":properties}
+  resp = root_resource.put(path=cpath , payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY") 
+
+
+
+class ConfigModel(BaseModel):
+  """
+  The ConfigModel class
+  """
+  RO_ATTR = ('properties',)
+  RW_ATTR = ('tag', 'type')
+  REF_ATTR = ('cluster_name',)
+  
+  def __init__(self, resource_root, tag , type=None):
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<ConfigModel>> tag = %s; type = %s" % (self.tag, self.type)
+  
+  def _get_cluster_name(self):
+    if self.clusterRef:
+      return self.clusterRef.cluster_name
+    return None
+
+  def __lt__(self, other):
+    return self.tag < other.tag
+
+  def _path(self):
+    """
+    Return the API path for this service.
+    """
+    if self._get_cluster_name():
+      return paths.CONFIGURATION_PATH % (self._get_cluster_name(), self.type , self.tag)
+    else:
+      return ''

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/host.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/host.py b/ambari-client/python-client/src/main/python/ambari_client/model/host.py
new file mode 100755
index 0000000..f261deb
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/host.py
@@ -0,0 +1,255 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+from ambari_client.model.base_model import  BaseModel , ModelList
+from ambari_client.model import status , component , paths , utils
+
+
+
+LOG = logging.getLogger(__name__)
+
+
+def _get_host(root_resource, host_name):
+  """
+  Lookup up by host_name
+  @param root_resource: The root Resource object.
+  @param cluster_name: Cluster name
+  @param host_name: Host name
+  @return: A HostModel object
+  """
+  path = paths.HOST_PATH % (host_name)
+  dic = root_resource.get(path)
+  
+  return utils.ModelUtils.create_model(HostModel , dic, root_resource, "Hosts")
+
+def _get_cluster_host(root_resource, cluster_name , host_name):
+  """
+  Lookup cluster host up by host_name
+  @param root_resource: The root Resource object.
+  @param cluster_name: Cluster name
+  @param host_name: Host name
+  @return: A HostModel object
+  """
+  path = paths.CLUSTER_HOST_PATH % (cluster_name, host_name)
+  dic = root_resource.get(path)
+  return utils.ModelUtils.create_model(HostModel , dic, root_resource, "Hosts")
+
+
+
+def _create_hosts(root_resource, host_list):
+  """
+  Create hosts from list
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @param ip: IP address
+  @param rack_info: Rack id. Default None
+  @return: An HostList object
+  """
+  
+  data = [{"Hosts":{"host_name":x.host_name,"ip":x.ip,"rack_info":x.rack_info}} 
+          for x in host_list]
+  resp = root_resource.post(paths.HOSTS_PATH, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+def _create_host(root_resource, host_name, ip, rack_info=None):
+  """
+  Create a host
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @param ip: IP address
+  @param rack_info: Rack id. Default None
+  @return: An HostModel object
+  """
+  host_list = ModelList([HostModel(root_resource, host_name, ip, rack_info)])
+  return _create_hosts(root_resource, host_list)
+
+def _add_hosts(root_resource, cluster_name , host_list):
+  """
+  Adds a hosts to a cluster.
+  @param root_resource: The root Resource object.
+  @param cluster_name: Cluster name
+  @param host_list: list of hosts
+  @return: A StatusModel object
+  """
+  cpath = paths.HOSTS_CREATE_PATH % (cluster_name)
+  data = [{"Hosts":{"host_name":x.host_name,"ip":x.ip,"rack_info":x.rack_info}} 
+          for x in host_list]
+  resp = root_resource.post(path=cpath, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _add_host(root_resource, cluster_name , host_name , ip, rack_info=None):
+  """
+  Adds a host to a cluster.
+  @param host_name: Host name
+  @param ip: ip of Host 
+  @param rack_info: rack information
+  @return: StatusModel.
+  """
+  host_list = ModelList([HostModel(root_resource, host_name, ip, rack_info)])
+  return _add_hosts(root_resource, cluster_name, host_list)
+
+
+def _assign_role(root_resource, cluster_name , host_name , component_name):
+  """
+  Add a new component to a node
+  @param root_resource: The root Resource object.
+  @param cluster_name: Cluster name
+  @param component_name : name of component.
+  @param host_name: name of host
+  @return: StatusModel
+  """
+  data = {"host_components":[{"HostRoles":{"component_name":component_name}}]}
+  cpath = paths.HOSTS_ASSIGN_ROLE % (cluster_name, host_name)
+  resp = root_resource.post(path=cpath, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _get_all_hosts(root_resource):
+  """
+  Get all hosts
+  @param root_resource: The root Resource.
+  @return: A list of HostModel objects.
+  """
+  dic = root_resource.get(paths.HOSTS_PATH)
+  return utils.ModelUtils.get_model_list(ModelList, HostModel, dic, root_resource , "Hosts")
+  
+  
+def _get_all_cluster_hosts(root_resource, cluster_name):
+  """
+  Get all hosts in the cluster
+  @param root_resource: The root Resource.
+  @param cluster_name: The name of the cluster.
+  @return: A list of HostModel objects.
+  """
+  path = paths.CLUSTER_HOSTS_PATH % (cluster_name)
+  path = path + '?fields=*'
+  dic = root_resource.get(path)
+  return utils.ModelUtils.get_model_list(ModelList, HostModel, dic, root_resource , "Hosts")
+
+
+def _delete_host(root_resource, host_name):
+  """
+  Delete a host by id
+  @param root_resource: The root Resource object.
+  @param host_name: Host name
+  @return: StatusModel object
+  """
+  resp = root_resource.delete(paths.HOST_PATH % (host_name))
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+  
+
+def _delete_cluster_host(root_resource, cluster_name , host_name):
+  """
+  Delete a host by id
+  @param root_resource: The root Resource object.
+  @param host_name: Host name
+  @param cluster_name: cluster name
+  @return: StatusModel object
+  """
+  path = paths.CLUSTER_HOST_PATH % (cluster_name, host_name)
+  resp = root_resource.delete(path)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _bootstrap_hosts(root_resource , hosts_list, ssh_key):
+  """
+  Bootstrap hosts.
+  @param hosts_list list of host_names.
+  @return: A  StatusModel object.
+  """
+  payload_dic = {'sshKey':ssh_key.encode('string_escape') , 'hosts':hosts_list}
+  resp = root_resource.post(paths.BOOTSTRAP_PATH, payload_dic , content_type="application/json")
+  status_dict = _bootstrap_resp_to_status_dict(resp)
+  return utils.ModelUtils.create_model(status.StatusModel, status_dict, root_resource, "NO_KEY")
+
+def _bootstrap_resp_to_status_dict(resp):
+  """
+  Bootstrap response has a little odd format
+  that's why we have to convert it to the normal
+  format to handle it properly later.
+  """
+  
+  # if we got other response, like an error 400 happened on higher level
+  if isinstance( resp['status'], int ):
+    return resp
+  
+  new_resp = {}
+  
+  if resp['status'] == "OK":
+    new_resp['status'] = 201
+  else: # ERROR
+    new_resp['status'] = 500
+    
+  new_resp['message'] = resp['log']
+  new_resp['requestId'] = resp['requestId']
+  return new_resp
+
+
+ 
+
+
+class HostModel(BaseModel):
+  """
+  The HostModel class
+  """
+  RO_ATTR = ('host_state', 'public_host_name')
+  RW_ATTR = ('host_name', 'ip', 'rack_info')
+  REF_ATTR = ('cluster_name',)
+  
+  def __init__(self, resource_root, host_name, ip=None , rack_info='/default-rack'):
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<HostModel>> hostname = %s; ip = %s ; rack_info = %s" % (self.host_name, self.ip, self.rack_info)
+
+  def _get_cluster_name(self):
+    if self.clusterRef:
+      return self.clusterRef.cluster_name
+    return None
+
+  def _path(self):
+    return paths.HOSTS_PATH + '/' + self.host_name
+
+  def get_host_components(self, detail=None):
+    """
+    Get a specific host's components.
+    @return: A ModelList containing ComponentModel objects.
+    """
+    return component.get_host_components(self._get_resource_root(), self._get_cluster_name(), self.host_name)
+
+  def get_host_component(self, component_name , detail=None):
+    """
+    Get a specific host's ,specific component.
+    @param component_name : name of component.
+    @return: A ComponentModel object.
+    """
+    return component.get_host_component(self._get_resource_root(), self._get_cluster_name(), self.host_name , component_name)
+
+  def assign_role(self, component_name , detail=None):
+    """
+    Assign a component role to the host
+    @param component_name : name of component.
+    @return: StatusModel.
+    """
+    return _assign_role(self._get_resource_root(), self._get_cluster_name(), self.host_name , component_name)
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/paths.py b/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
new file mode 100755
index 0000000..f34c26d
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
@@ -0,0 +1,50 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+CLUSTERS_PATH = "/clusters"
+CLUSTERS_CONFIG_PATH = "/clusters/%s"
+CLUSTER_HOSTS_PATH = "/clusters/%s/hosts"
+CLUSTER_HOST_PATH = "/clusters/%s/hosts/%s"
+CLUSTER_START_ALL_SERVICES = "/clusters/%s/services?ServiceInfo/state=INSTALLED"
+CLUSTER_STOP_ALL_SERVICES = "/clusters/%s/services?ServiceInfo"
+
+
+SERVICES_PATH = "/clusters/%s/services"
+SERVICE_PATH = "/clusters/%s/services/%s"
+SERVICE_CREATE_PATH = "/clusters/%s/services/?ServiceInfo/service_name=%s"
+SERVICE_COMPONENTS_PATH = "/clusters/%s/services/%s/components?fields=*"
+SERVICE_COMPONENT_PATH = "/clusters/%s/services/%s/components/%s"
+
+
+HOST_PATH = "/hosts/%s"
+HOSTS_PATH = "/hosts"
+HOSTS_CREATE_PATH = "/clusters/%s/hosts"
+HOSTS_COMPONENTS_PATH = "/clusters/%s/hosts/%s/host_components?fields=HostRoles/state"
+HOSTS_COMPONENT_PATH = "/clusters/%s/hosts/%s/host_components/%s" 
+HOSTS_ASSIGN_ROLE = "/clusters/%s/hosts?Hosts/host_name=%s"
+
+BOOTSTRAP_PATH = "/bootstrap"
+REQUEST_STATUS_PATH = "/clusters/%s/requests/%s?fields=tasks/Tasks/status"
+REQUEST_PATH = "clusters/%s/requests/%s"
+
+CONFIGURATION_PATH = "/clusters/%s/configurations?type=%s&tag=%s"
+CONFIGURATION_ALL_PATH = "/clusters/%s/configurations?type=%s"
+CREATE_CONFIGURATION_PATH = "/clusters/%s/configurations"
+UPDATE_CONFIGURATION_PATH="/clusters/%s"
+
+STACK_SERVICES_COMPONENTS_PATH = "/stacks2/HDP/versions/%s/stackServices/%s/serviceComponents?fields=*"
+STACK_SERVICES_CONFIG_PATH = "/stacks2/HDP/versions/%s/stackServices/%s/configurations?fields=*"

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/service.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/service.py b/ambari-client/python-client/src/main/python/ambari_client/model/service.py
new file mode 100755
index 0000000..ed91d7c
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/service.py
@@ -0,0 +1,203 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import logging
+import time
+from ambari_client.model.base_model import  BaseModel , ModelList
+from ambari_client.model import component , paths , status , stack , utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+
+def _get_all_services(resource_root, cluster_name):
+  """
+  Get all services in a cluster.
+  @param cluster_name :Cluster name.
+  @return: A  ModelList object.
+  """
+  path = paths.SERVICES_PATH % (cluster_name,)
+  path = path + '?fields=*'
+  dic = resource_root.get(path)
+  return utils.ModelUtils.get_model_list(ModelList, ServiceModel, dic, resource_root , "ServiceInfo")
+
+
+def _get_service(resource_root, service_name, cluster_name):
+  """
+  Get a specific services in a cluster.
+  @param service_name :Service name.
+  @param cluster_name :Cluster name.
+  @return: A  ServiceModel object.
+  """
+  path = "%s/%s" % (paths.SERVICES_PATH % (cluster_name,), service_name)
+  dic = resource_root.get(path)
+  return utils.ModelUtils.create_model(ServiceModel , dic, resource_root, "ServiceInfo") 
+
+
+def _create_services(root_resource, cluster_name , service_names):
+  """
+  Create services
+  @param root_resource: The root Resource object.
+  @param service_names: list of service_names
+  @param cluster_name: Cluster name
+  @return: StatusModel
+  """
+  data = [{"ServiceInfo":{"service_name":x}} for x in service_names]
+  cpath = paths.SERVICES_PATH % cluster_name
+  resp = root_resource.post(path=cpath, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _create_service(root_resource, cluster_name , service_name):
+  """
+  Create a single service
+  @param root_resource: The root Resource object.
+  @param service_name:  service_name
+  @param cluster_name: Cluster name
+  @return: StatusModel
+  """
+  data = {"ServiceInfo":{"service_name":service_name}} 
+  cpath = paths.SERVICES_PATH % cluster_name
+  resp = root_resource.post(path=cpath, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _create_service_components(root_resource, cluster_name , version , service_name):
+  """
+  Create service with components
+  @param root_resource: The root Resource object.
+  @param service_name:  service_names
+  @param cluster_name: Cluster service_name
+  @return: An ServiceModel object
+  """
+  components = stack._get_components_from_stack(root_resource, version , service_name)
+  list_componnetinfo = [{"ServiceComponentInfo":{"component_name":x.component_name }} for x in components]
+  data = {"components":list_componnetinfo}
+  cpath = paths.SERVICE_CREATE_PATH % (cluster_name, service_name)
+  resp = root_resource.post(path=cpath, payload=data)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _create_service_component(root_resource, cluster_name , version , service_name, component_name):
+  """
+  Create service with single component
+  @param root_resource: The root Resource object.
+  @param service_name:  service_names
+  @param cluster_name: Cluster service_name
+  @param component_name: name of component
+  @return: An ServiceModel object
+  """
+  cpath = paths.SERVICE_COMPONENT_PATH % (cluster_name, service_name, component_name)
+  resp = root_resource.post(path=cpath, payload=None)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+
+
+def _delete_service(root_resource, service_name, cluster_name):
+  """
+  Delete a service by service_name
+  @param root_resource: The root Resource object.
+  @param service_name: Service service_name
+  @param cluster_name: Cluster service_name
+  @return: The StatusModel object
+  """
+  resp = root_resource.delete("%s/%s" % (paths.SERVICES_PATH % (cluster_name,), service_name))
+  time.sleep(3)
+  return utils.ModelUtils.create_model(status.StatusModel, resp, root_resource, "NO_KEY")
+   
+   
+    
+class ServiceModel(BaseModel):
+  """
+  The ServiceModel class
+  """
+  #RO_ATTR = ('state', 'cluster_name')
+  RW_ATTR = ('service_name', 'state')
+  REF_ATTR = ('cluster_name',)
+
+  def __init__(self, resource_root, service_name , state):
+    #BaseModel.__init__(self, **locals())
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<ServiceModel>> = %s ;state = %s ; cluster_name = %s" % (self.service_name, self.state , self._get_cluster_name())
+
+  def _get_cluster_name(self):
+    if self.clusterRef:
+      return self.clusterRef.cluster_name
+    return None
+
+  def _path(self):
+    """
+    Return the API path for this object.
+    """
+    if self._get_cluster_name():
+      return paths.SERVICE_PATH % (self._get_cluster_name(), self.service_name)
+    else:
+      return ''
+
+  def _action(self, data=None):
+    path = self._path() 
+    resp = self._get_resource_root().put(path, payload=data)
+    status_model = utils.ModelUtils.create_model(status.StatusModel, resp, self._get_resource_root(), "NO_KEY")
+    if status_model._get_id() is not None:
+      status_model.request_path = paths.REQUEST_PATH % (self._get_cluster_name(), status_model._get_id())
+    else:
+      status_model.request_path = None
+    return status_model
+
+  def start(self ,message = None):
+    """
+    Start a service.
+    """
+    data = None
+    if message:
+        data = {"RequestInfo":{"context":message},"Body":{"ServiceInfo":{"state":"STARTED"}}}
+    else:
+        data = {"ServiceInfo": {"state": "STARTED"}}
+    return self._action(data)
+
+  def stop(self ,message = None):
+    """
+    Stop a service.
+    """
+    data = None
+    if message:
+        data = {"RequestInfo":{"context":message},"Body":{"ServiceInfo":{"state":"INSTALLED"}}}
+    else:
+        data = {"ServiceInfo": {"state": "INSTALLED"}}
+    return self._action(data)
+
+  def install(self):
+    """
+    Install a service.
+    """
+    data = {"ServiceInfo": {"state": "INSTALLED"}}
+    return self._action(data)
+
+  def get_service_components(self, detail=None):
+    """
+    Get a specific services's components.
+    @return: A ComponentModel object.
+    """
+    return component._get_service_components(self._get_resource_root(), self._get_cluster_name(), self.service_name)
+
+  def get_service_component(self, component_name , detail=None):
+    """
+    Get a specific services's components.
+    @return: A ComponentModel object.
+    """
+    return component._get_service_component(self._get_resource_root(), self._get_cluster_name(), self.service_name, component_name)

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/stack.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/stack.py b/ambari-client/python-client/src/main/python/ambari_client/model/stack.py
new file mode 100755
index 0000000..40088f7
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/stack.py
@@ -0,0 +1,79 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+
+from ambari_client.model.base_model import  BaseModel , ModelList
+from ambari_client.model import paths , utils
+
+
+def _get_configuration_from_stack(resource_root, version , service_name , tag="version1"):
+  """
+  Get configuration from stack
+  @param resource_root: The root Resource .
+  @param cluster_name: cluster_name
+  @param type: type of config
+  @return: A ModelList of ConfigModel object
+  """
+  dic = resource_root.get(paths.STACK_SERVICES_CONFIG_PATH % (version, service_name))
+  return utils.ModelUtils.get_model_list(ModelList, StackConfigModel, dic, resource_root , "StackConfigurations")
+
+
+def _get_components_from_stack(resource_root, version , service_name , tag="version1"):
+  """
+  Get configuration from stack
+  @param resource_root: The root Resource .
+  @param cluster_name: cluster_name
+  @param type: type of config
+  @return: A ModelList of ConfigModel object
+  """
+  path = paths.STACK_SERVICES_COMPONENTS_PATH % (version, service_name)
+  dic = resource_root.get(path)
+  return utils.ModelUtils.get_model_list(ModelList, StackComponentModel, dic, resource_root , "StackServiceComponents")
+
+
+class StackConfigModel(BaseModel):
+  """
+  The StackConfigModel class
+  """
+  RO_ATTR = ('stack_name', 'type', 'property_description')
+  RW_ATTR = ('property_name', 'property_value', 'service_name', 'stack_version')
+  REF_ATTR = ()
+  
+  def __init__(self, resource_root, property_name , property_value=None , service_name=None , stack_version=None):
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<StackConfigModel>> property_name=%s; property_value=%s ;service_name= %s" % (self.property_name, self.property_value, self.service_name)
+  
+
+
+
+
+class StackComponentModel(BaseModel):
+  """
+  The StackComponentModel class
+  """
+  RO_ATTR = ('stack_name', 'is_master', 'is_client', 'component_category')
+  RW_ATTR = ('component_name', 'service_name', 'stack_version')
+  REF_ATTR = ()
+  
+  def __init__(self, resource_root, component_name , service_name=None , stack_version=None):
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<StackComponentModel>> component_name = %s; service_name = %s" % (self.component_name, self.service_name)
+  

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/status.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/status.py b/ambari-client/python-client/src/main/python/ambari_client/model/status.py
new file mode 100755
index 0000000..f88a534
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/status.py
@@ -0,0 +1,71 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import logging
+from ambari_client.model.base_model import  BaseModel 
+from ambari_client.model import paths , utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+class StatusModel(BaseModel):
+  """
+  The ServiceModel class
+  """
+  RO_ATTR = ('id',)
+  RW_ATTR = ('status', 'requestId', "message")
+  REF_ATTR = ('cluster_name',)
+
+  def __init__(self, resource_root, status , requestId=None, message=None):
+    #BaseModel.__init__(self, **locals())
+    utils.retain_self_helper(BaseModel, **locals())
+
+  def __str__(self):
+    return "<<StatusModel>> status = %s ; requestId = %s ;message = %s" % (self._get_status(), self._get_id() , self.get_message())
+
+  def get_bootstrap_path(self):
+    return paths.BOOTSTRAP_PATH + '/' + str(self.requestId)
+
+  def get_request_path(self):
+    return self.request_path
+
+  def get_message(self):
+    if hasattr(self, 'message'):
+        return self.message
+    else:
+        None
+
+  def is_error(self):
+    return (self.status != 200 and self.status != 201 and self.status != 202)  
+        
+  def _get_id(self):
+    if hasattr(self, 'requestId') and self.requestId:
+        return self.requestId
+    elif hasattr(self, 'id') and self.id:
+        return self.id
+    else:
+        None
+
+  def _get_status(self):
+    if hasattr(self, 'status') and isinstance(self.status, basestring):
+        self.message = self.status
+        self.status = 200
+        return self.status
+    elif hasattr(self, 'status') and isinstance(self.status, int):
+        return self.status
+    else:
+        None
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/utils.py b/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
new file mode 100755
index 0000000..0b3b6a0
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
@@ -0,0 +1,231 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import logging
+import sys
+import unicodedata
+from ambari_client.core import errors
+
+LOG = logging.getLogger(__name__)
+
+
+ref_dic = {"cluster_name":"clusterRef"}
+ref_class_dic = {"ClusterModelRef":"cluster_name"}
+ref_pkg_dic = {"ClusterModelRef":"ambari_client.model.cluster"}
+LIST_KEY = "items"
+ALL="ALL" 
+  
+class ModelUtils(object):
+  
+  @staticmethod
+  def _check_is_error(expected_class, model_dict, resource_root):
+    from ambari_client.model.status import StatusModel
+
+    if model_dict.has_key("status"):
+      resp = ModelUtils.create_model(StatusModel, model_dict.copy(), resource_root, "NO_KEY", check_errors=False)
+      
+      if expected_class!=StatusModel or resp.is_error():
+        if resp.status in errors._exceptions_to_codes:
+          raise errors._exceptions_to_codes[resp.status](resp, resource_root)
+        else:
+          raise errors.UnknownServerError(resp, resource_root)
+
+  @staticmethod
+  def get_model_list(member_list_clss, member_cls, collection_dict, resource_root , RESOURCE_KEY_WORD, check_errors=True):
+    """
+    create a model.
+    @param member_list_clss : model_list class.
+    @param model_cls : model class.
+    @param collection_dict : collection dict used for creating the list of objects.
+    @param resource_root : resource object.
+    @param RESOURCE_KEY_WORD : tsake subset of model_dict based on this key.
+    @return: A  ModelList object.
+    """
+    if check_errors:
+      ModelUtils._check_is_error(member_list_clss, collection_dict, resource_root)
+    
+    #print locals()
+    json_list = []
+    
+    #remove items
+    if isinstance(collection_dict, dict) and collection_dict.has_key(LIST_KEY):
+        json_list = collection_dict[LIST_KEY]
+        LOG.debug("get_model_list: collection_dict is dict ? %s ; has_key = %s" % (isinstance(collection_dict, dict), collection_dict.has_key(LIST_KEY)))
+        LOG.debug ("get_model_list: collection_dict has %s ;subset = %s" % (LIST_KEY, str(json_list)))
+    else:
+        json_list = collection_dict
+        LOG.error("get_model_list: collection_dict is dict ? %s ; has_key = %s" % (isinstance(collection_dict, dict), collection_dict.has_key(LIST_KEY)))
+    
+    LOG.debug ("get_model_list: json_list  value : \n\t" + str(json_list))
+    if isinstance(json_list, list):
+        json_list_new = [ x.get(RESOURCE_KEY_WORD) for x in json_list]
+        LOG.debug("get_model_list: json_list is list ? %s ; " % (isinstance(json_list, list)))
+    else: 
+        json_list_new = [json_list]
+        LOG.error("get_model_list: json_list is list ? %s ; " % (isinstance(json_list, list)))
+    
+    LOG.debug ("get_model_list: json_list_new used for creating ModelList  \n\t" + str(json_list_new))
+    objects = [ ModelUtils.create_model(member_cls, x, resource_root , RESOURCE_KEY_WORD) for x in json_list_new ]
+    LOG.debug (objects)
+    return member_list_clss(objects)
+
+
+  @staticmethod
+  def create_model(model_cls, model_dict, resource_root, RESOURCE_KEY_WORD, check_errors=True):
+    """
+    create a model.
+    @param model_cls : model class.
+    @param model_dict : model dict used for creating the object.
+    @param resource_root : resource object.
+    @param RESOURCE_KEY_WORD : tsake subset of model_dict based on this key.
+    @return: A model_cls object.
+    """
+    if check_errors:
+      ModelUtils._check_is_error(model_cls, model_dict, resource_root)
+      
+    #print locals()
+    rw_dict = { }
+    LOG.debug ("model_dict =   " + str(model_dict))
+    
+    if isinstance(model_dict, dict) and model_dict.has_key(RESOURCE_KEY_WORD):
+        model_dict = model_dict[RESOURCE_KEY_WORD]
+        LOG.debug ("model_dict has %s ;subset = %s" % (RESOURCE_KEY_WORD, str(model_dict.items())))
+    if isinstance(model_dict, dict) and model_dict.has_key("Requests"):
+        model_dict = model_dict["Requests"]
+        LOG.debug ("model_dict has Requests ;subset = %s" % (str(model_dict.items())))
+     
+      
+    for k, v in model_dict.items():
+      LOG.debug("key = %s ; value = %s " % (str(k), str(v)))
+      if k in model_cls.RW_ATTR:
+        LOG.debug (k + " is there in RW_ATTR")
+        rw_dict[k] = v
+        del model_dict[k]
+
+    rw_dict = get_unicode_kw(rw_dict)
+    obj = model_cls(resource_root, **rw_dict)
+
+    for attr in model_cls.RO_ATTR:
+      obj._setattr(attr, None)
+
+    for k, v in model_dict.items():
+      if k in model_cls.RO_ATTR:
+        obj._setattr(k, v)
+      else:
+        LOG.debug("Unexpected attribute '%s' in %s json" % (k, model_cls.__name__))
+
+    for attr in model_cls.REF_ATTR:
+      LOG.debug("%s found as reference var" % (attr))
+      obj._setattr(getREF_class_name(attr), None)
+
+    for k, v in model_dict.items():
+      if k in model_cls.REF_ATTR:
+        obj._setattr(getREF_class_name(k), v)
+      else:
+        LOG.debug("Unknown attribute '%s' found in model_dict for %s " % (k, model_cls.__name__))
+    return obj
+
+
+#get attribute with REF
+def getREF_class_name(REF_name):
+  if ref_dic.has_key(REF_name):
+    return ref_dic[str(REF_name)]
+  else:
+    return None
+  
+
+def getREF_var_name(REF_name):
+  if ref_class_dic.has_key(REF_name):
+    return ref_class_dic[str(REF_name)]
+  else:
+    return None
+
+
+def get_REF_object(ref_class_name):
+  """
+  Gets the Ref object based on class_name
+  """
+  class_ref = getattr(sys.modules[ref_pkg_dic[ref_class_name]], ref_class_name)
+  LOG.debug(class_ref)
+  return class_ref  
+
+
+def get_unicode(v):
+  #import unicodedata
+  if v:
+    if isinstance(v, unicode):
+      v = unicodedata.normalize('NFKD', v).encode('ascii', 'ignore')
+      LOG.debug(v)
+    elif isinstance(v, str):
+      LOG.debug("warning: string found while expecting unicode %s" % v)
+  return v
+
+
+def retain_self_helper(memclass, self=None, **kwargs):
+    #print locals()
+    #from ambari_client.model.base_model import  BaseModel 
+    memclass.__init__(self, **kwargs)
+
+
+def get_unicode_kw(dic):
+  """
+  We use unicode strings as keys in kwargs.
+  """
+  res = { }
+  for k, v in dic.iteritems():
+    res[str(k)] = v
+  return res
+
+
+def get_config_type(service_name):
+  """
+  get the config type based on service_name
+  """
+  if service_name == "HDFS":
+      type = "hdfs-site"
+  elif service_name == "HDFS":
+      type = "core-site"
+  elif service_name == "MAPREDUCE":
+      type = "mapred-site"
+  elif service_name == "HBASE":
+      type = "hbase-site"
+  elif service_name == "OOZIE":
+      type = "oozie-site"
+  elif service_name == "HIVE":
+      type = "hive-site"
+  elif service_name == "WEBHCAT":
+      type = "webhcat-site"
+  else:
+      type = "global"
+  return type
+      
+      
+def get_key_value(dictt , key):
+  """
+  Search for some random key in the dict
+  """
+  if isinstance(dictt, dict) and dictt.has_key(key):
+    return dictt[key]
+  elif isinstance(dictt, dict) and not dictt.has_key(key):
+    #check if values has it?
+    for v in dictt.values():
+      if isinstance(v, dict):
+        return get_key_value(v, key)
+      elif isinstance(v, list):
+        for l in list:
+          return get_key_value(l, key)
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/resources/__init__.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/resources/__init__.py b/ambari-client/python-client/src/main/python/ambari_client/resources/__init__.py
new file mode 100755
index 0000000..278df2e
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/resources/__init__.py
@@ -0,0 +1,16 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/resources/clusters.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/resources/clusters.py b/ambari-client/python-client/src/main/python/ambari_client/resources/clusters.py
new file mode 100755
index 0000000..ca3cca6
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/resources/clusters.py
@@ -0,0 +1,58 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+from ambari_client.model import  cluster
+__docformat__ = "epytext"
+
+
+def _get_cluster(resource_root, cluster_name):
+  """
+  Get a cluster by cluster_name
+  @param resource_root: The root Resource.
+  @param cluster_name: Cluster's name
+  @return: ClusterModel object
+  """
+  return cluster._get_cluster(resource_root, cluster_name)
+
+
+def _get_all_clusters(root_resource):
+  """
+  Get all clusters in Ambari.
+  @param root_resource: The root Resource object.
+  @return: A list of ClusterModel objects in ModelList.
+  """
+  return cluster._get_all_clusters(root_resource)
+
+ 
+def _create_cluster(root_resource, cluster_name, version):
+  """
+  Create a cluster
+  @param root_resource: The root Resource.
+  @param cluster_name: Cluster cluster_name
+  @param version: HDP version
+  @return: An ClusterModel object
+  """
+  return cluster._create_cluster(root_resource, cluster_name, version)
+  
+  
+def _delete_cluster(root_resource, cluster_name):
+  """
+  Create a cluster
+  @param root_resource: The root Resource.
+  @param cluster_name: Cluster cluster_name
+  """
+  return cluster._delete_cluster(root_resource, cluster_name)

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/resources/hosts.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/resources/hosts.py b/ambari-client/python-client/src/main/python/ambari_client/resources/hosts.py
new file mode 100755
index 0000000..0d53711
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/resources/hosts.py
@@ -0,0 +1,127 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+from ambari_client.model import  host
+__docformat__ = "epytext"
+
+def _add_hosts(root_resource, cluster_name , host_list):
+  """
+  Adds a hosts to a cluster.
+  @param root_resource: The root Resource object.
+  @param cluster_name: Cluster name
+  @param host_list: list of hosts
+  @return: A StatusModel object
+  """
+  return _add_hosts(root_resource, cluster_name , host_list)
+
+def _add_host(root_resource, cluster_name , host_name , ip, rack_info=None):
+  """
+  Adds a host to a cluster.
+  @param host_name: Host name
+  @param ip: ip of Host 
+  @param rack_info: rack information
+  @return: StatusModel.
+  """
+  return _add_host(root_resource, cluster_name , host_name , ip, rack_info)
+
+
+def _create_hosts(root_resource, host_list):
+  """
+  Create a host
+  @param root_resource: The root Resource.
+  @param host_list: ModelList list of hosts
+  @return: A HostModel object
+  """
+  return host._create_hosts(root_resource, host_list)
+
+def _create_host(root_resource, host_name, ip, rack_info=None):
+  """
+  Create a host
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @param ip: IP address
+  @param rack_info: Rack id. Default None
+  @return: A HostModel object
+  """
+  return host._create_host(root_resource, host_name, ip, rack_info)
+
+
+def _get_host(root_resource, host_name):
+  """
+  Lookup a host by name
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @return: A HostModel object
+  """
+  return host._get_host(root_resource, host_name)
+
+def _get_cluster_host(root_resource, cluster_name, host_name):
+  """
+  Lookup a host by name
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @return: A HostModel object
+  """
+  return host._get_cluster_host(root_resource, cluster_name, host_name)
+
+
+
+
+def _get_all_hosts(root_resource):
+  """
+  Get all hosts
+  @param root_resource: The root Resource.
+  @return: A list of HostModel objects.
+  """
+  return host._get_all_hosts(root_resource)
+
+def _get_all_cluster_hosts(root_resource, cluster_name):
+  """
+  Get all cluster hosts
+  @param root_resource: The root Resource.
+  @return: A list of HostModel objects.
+  """
+  return host._get_all_cluster_hosts(root_resource, cluster_name)
+
+
+def _delete_host(root_resource, host_name):
+  """
+  Delete a host by id
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @return: The deleted HostModel object
+  """
+  return host._delete_host(root_resource, host_name)
+
+def _delete_cluster_host(root_resource, cluster_name , host_name):
+  """
+  Delete a cluster host by id
+  @param root_resource: The root Resource.
+  @param host_name: Host name
+  @return: The deleted HostModel object
+  """
+  return host._delete_cluster_host(root_resource, cluster_name , host_name)
+
+
+def _bootstrap_hosts(root_resource , hosts_list , ssh_key):
+  """
+  Bootstrap hosts.
+  @param hosts_list: list of host_names.
+  @param ssh_key: ssh key for password-less access.
+  @return: A  StatusModel object.
+  """
+  return host._bootstrap_hosts(root_resource, hosts_list , ssh_key)

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/ambari_client/resources/stacks.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/resources/stacks.py b/ambari-client/python-client/src/main/python/ambari_client/resources/stacks.py
new file mode 100755
index 0000000..f4ef518
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/ambari_client/resources/stacks.py
@@ -0,0 +1,41 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+from ambari_client.model import   stack , component
+
+__docformat__ = "epytext"
+
+
+def _get_config(root_resource, version, service_name):
+  """
+  Get service configurations from stack
+  @param version: The HDP version.
+  @param service_name: service name
+  @return: A ConfigModel object
+  """
+  return stack._get_configuration_from_stack(root_resource, version, service_name)
+
+
+def _get_components(root_resource, version, service_name):
+  """
+  Get service components from stack
+  @param version: The HDP version.
+  @param service_name: service name
+  @return: A ComponentModel object
+  """
+  return stack._get_components_from_stack(root_resource, version, service_name)
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/main/python/setup.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/setup.py b/ambari-client/python-client/src/main/python/setup.py
new file mode 100755
index 0000000..24942ad
--- /dev/null
+++ b/ambari-client/python-client/src/main/python/setup.py
@@ -0,0 +1,39 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+
+from setuptools import setup, find_packages
+
+from sys import version_info, platform
+
+if version_info[:2] > (2, 5):
+    install_requires = []
+else:
+    install_requires = ['simplejson >= 2.0.0']
+
+# Python 2.6 and below requires argparse
+if version_info[:2] < (2, 7):
+    install_requires += ['argparse']
+
+setup(
+  name = 'ambari_client',
+  author_email = "ambari-dev@incubator.apache.org",
+  version = "1.0.3-SNAPSHOT",
+  packages = ['ambari_client'],
+  install_requires = install_requires,
+  description = 'Ambari python REST API client',
+  license = 'Apache License 2.0'
+)

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/packages/tarball/all.xml
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/packages/tarball/all.xml b/ambari-client/python-client/src/packages/tarball/all.xml
new file mode 100755
index 0000000..0e4f34b
--- /dev/null
+++ b/ambari-client/python-client/src/packages/tarball/all.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+  <!--This 'all' id is not appended to the produced bundle because we do this:
+    http://maven.apache.org/plugins/maven-assembly-plugin/faq.html#required-classifiers
+  -->
+  <formats>
+    <format>dir</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>src/main/python</directory>
+      <outputDirectory>/</outputDirectory>
+    </fileSet>
+  </fileSets>
+</assembly>

http://git-wip-us.apache.org/repos/asf/ambari/blob/11dd9df8/ambari-client/python-client/src/test/python/TestAmbariClient.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/test/python/TestAmbariClient.py b/ambari-client/python-client/src/test/python/TestAmbariClient.py
new file mode 100755
index 0000000..1831f89
--- /dev/null
+++ b/ambari-client/python-client/src/test/python/TestAmbariClient.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+
+'''
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+'''
+
+
+from mock.mock import MagicMock, patch
+from ambari_client.ambari_api import  AmbariClient 
+from HttpClientInvoker import HttpClientInvoker
+from ambari_client.model.stack import StackConfigModel, StackComponentModel
+import unittest
+import logging
+
+class TestAmbariClient(unittest.TestCase):
+
+  def setUp(self):
+    http_client_logger = logging.getLogger()
+    http_client_logger.info('Running test:' + self.id())
+
+  def create_client(self, http_client_mock = MagicMock()):
+    http_client_mock.invoke.side_effect = HttpClientInvoker.http_client_invoke_side_effects
+    client = AmbariClient("localhost", 8080, "admin", "admin", version=1, client=http_client_mock)
+    return client
+
+  def test_init(self):
+    """
+    AmbariClient is the top-level root resources.
+    This testcase checks if when the  init method was called &
+    the httpclient was initialized
+    """
+    client = AmbariClient("localhost", 8080, "admin", "admin", version=1)
+    self.assertEqual(client.version, 1, "version should be 1")
+    self.assertEqual(client.host_url, "http://localhost:8080/api/v1",
+                       "host_url should be http://localhost:8080/api/v1")
+      
+    client = AmbariClient(host_name="localhost", user_name="admin", password="admin")
+    self.assertEqual(client.version, 1, "version should be 1")
+    self.assertEqual(client.host_url, "http://localhost:8080/api/v1",
+                       "host_url should be http://localhost:8080/api/v1")
+      
+    client = AmbariClient(host_name="localhost")
+    self.assertEqual(client.version, 1, "version should be 1")
+    self.assertEqual(client.host_url, "http://localhost:8080/api/v1",
+                       "host_url should be http://localhost:8080/api/v1")
+    
+    
+    client = AmbariClient("localhost", 8443, "admin", "admin", use_https=True)
+    self.assertEqual(client.version, 1, "version should be 1")
+    self.assertEqual(client.host_url, "https://localhost:8443/api/v1",
+                       "host_url should be https://localhost:8443/api/v1")
+      
+  def test_get_all_clusters(self):
+    """
+    Get all clusters.
+    This testcase checks if get_all_clusters returns a list of ModelList.
+    """
+    expected_output = {'items': [{'cluster_name': u'test1', 'version': u'HDP-1.2.1'}]}
+      
+    client = self.create_client()
+    all_clusters = client.get_all_clusters()
+      
+    self.assertEqual(len(all_clusters), 1)
+    self.assertEqual(all_clusters.to_json_dict(), expected_output)
+    
+  def test_get_cluster(self):
+    """
+    Get all clusters.
+    This testcase checks if get_all_clusters returns a list of ModelList.
+    """
+    expected_dict_output = {'cluster_name': u'test1', 'version': u'HDP-1.2.1'}
+    
+    client = self.create_client()
+    cluster = client.get_cluster('test1')
+    
+    self.assertEqual(cluster.cluster_name, "test1", "cluster_name should be test1 ")
+    self.assertEqual(cluster.to_json_dict(), expected_dict_output, "to_json_dict should convert ClusterModel")
+    
+  def test_get_host(self):
+    """
+    Get host
+    This testcase checks if client.get_host returns a correct host
+    """
+    expected_dict_output = {'ip': '10.0.2.15', 'host_name': 'dev06.hortonworks.com', 'rack_info': '/default-rack'}
+    
+    client = self.create_client()
+    host = client.get_host('dev06.hortonworks.com')
+    
+    self.assertEqual(host.to_json_dict(), expected_dict_output)
+    self.assertEqual(host.host_state, "HEARTBEAT_LOST")
+     
+  def test_get_all_hosts(self):
+    """
+    Get all hosts.
+    This testcase checks if get_all_hosts returns a list of ModelList.
+    """
+    expected_hosts_dict = {'items': [{'ip': None, 'host_name': u'apspal44-83', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-84', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-85', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-86', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-87', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-88', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'apspal44-89', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'r01hn01', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'r01mgt', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'r01wn01', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'r01wn02', 'rack_info': '/default-rack'}, {'ip': None, 'host_name': u'r01wn03', 'rack_info': '/default-rack'}]}
+      
+    client = self.create_client()
+    all_hosts = client.get_all_hosts()
+    
+    self.assertEqual(len(all_hosts), 12, "There should be 12 hosts from the response")
+    self.assertEqual(all_hosts.to_json_dict(), expected_hosts_dict)
+    
+  def test_bootstrap_hosts(self):
+    """
+    Test Bootstrap
+    """
+    http_client_mock = MagicMock()
+    
+    ssh_key = 'abc!@#$%^&*()_:"|<>?[];\'\\./'
+    host_list = ['dev05.hortonworks.com','dev06.hortonworks.com']
+    
+    expected_path = '//bootstrap'
+    expected_headers = {'Content-Type': 'application/json'}
+    expected_request = {'hosts': host_list, 'sshKey': 'abc!@#$%^&*()_:"|<>?[];\\\'\\\\./'}
+    expected_response = {'status': 201, 'message': u'Running Bootstrap now.', 'requestId': 5}
+                               
+    client = self.create_client(http_client_mock)
+    resp = client.bootstrap_hosts(host_list, ssh_key)
+
+    self.assertEqual(resp.to_json_dict(), expected_response)
+    http_client_mock.invoke.assert_called_with('POST', expected_path, headers=expected_headers, payload=expected_request)
+  
+  def test_create_cluster(self):
+    """
+    Test create cluster
+    """
+    http_client_mock = MagicMock()
+    
+    expected_path = '//clusters/c1'
+    expected_request = {'Clusters': {'version': 'HDP-2.0.5'}}
+          
+    client = self.create_client(http_client_mock)
+    resp = client.create_cluster('c1', 'HDP-2.0.5')
+    
+    http_client_mock.invoke.assert_called_with('POST', expected_path, headers=None, payload=expected_request)
+    
+  def test_delete_cluster(self):
+    """
+    Test create cluster
+    """
+    http_client_mock = MagicMock()
+    
+    expected_path = '//clusters/c1'
+    expected_request = None
+          
+    client = self.create_client(http_client_mock)
+    resp = client.delete_cluster('c1')
+    
+    http_client_mock.invoke.assert_called_with('DELETE', expected_path, headers=None, payload=expected_request)
+    
+  def test_delete_host(self):
+    """
+    Test delete host
+    """
+    http_client_mock = MagicMock()
+    
+    expected_path = '//hosts/abc.abc.abc'
+    expected_request = None
+          
+    client = self.create_client(http_client_mock)
+    resp = client.delete_host('abc.abc.abc')
+    
+    http_client_mock.invoke.assert_called_with('DELETE', expected_path, headers=None, payload=expected_request)
+    
+  def test_get_config(self):
+    """
+    Test get config
+    """
+    expected_dict = {'items': [{'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'datanode_du_reserved', 'property_value': u'1'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.access.time.precision', 'property_value': u'0'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.balance.bandwidthPerSec', 'property_value': u'6250000'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.block.access.token.enable', 'property_value': u'true'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.block.size', 'property_value': u'134217728'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.blockreport.initialDelay', 'property_value': u'120'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.cluster.administrators', 'property_value': u' hdfs'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'df
 s.datanode.du.pct', 'property_value': u'0.85f'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.datanode.failed.volumes.tolerated', 'property_value': u'0'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.datanode.ipc.address', 'property_value': u'0.0.0.0:8010'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.datanode.max.xcievers', 'property_value': u'4096'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.datanode.socket.write.timeout', 'property_value': u'0'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.heartbeat.interval', 'property_value': u'3'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.https.port', 'property_value': u'50470'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.namenode.avoid.read.stale.datanode', 'property_value': u'true'}, {'stack_version': u'1.3.0', 'servi
 ce_name': u'HDFS', 'property_name': u'dfs.namenode.avoid.write.stale.datanode', 'property_value': u'true'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.namenode.handler.count', 'property_value': u'100'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.namenode.handler.count', 'property_value': u'40'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.namenode.stale.datanode.interval', 'property_value': u'30000'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.namenode.write.stale.datanode.ratio', 'property_value': u'1.0f'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.permissions', 'property_value': u'true'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.permissions.supergroup', 'property_value': u'hdfs'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.replication.max', 'property_v
 alue': u'50'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.safemode.threshold.pct', 'property_value': u'1.0f'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.secondary.https.port', 'property_value': u'50490'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.umaskmode', 'property_value': u'077'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs.web.ugi', 'property_value': u'gopher,gopher'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_block_local_path_access_user', 'property_value': u'hbase'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_data_dir', 'property_value': u'/hadoop/hdfs/data'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_datanode_address', 'property_value': u'50010'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_datanode_data_dir_perm'
 , 'property_value': u'750'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_datanode_failed_volume_tolerated', 'property_value': u'0'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_datanode_http_address', 'property_value': u'50075'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_name_dir', 'property_value': u'/hadoop/hdfs/namenode'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_replication', 'property_value': u'3'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dfs_webhdfs_enabled', 'property_value': u'true'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'dtnode_heapsize', 'property_value': u'1024'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs.checkpoint.edits.dir', 'property_value': u'${fs.checkpoint.dir}'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs
 .checkpoint.period', 'property_value': u'21600'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs.checkpoint.size', 'property_value': u'536870912'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs.trash.interval', 'property_value': u'360'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs_checkpoint_dir', 'property_value': u'/hadoop/hdfs/namesecondary'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs_checkpoint_period', 'property_value': u'21600'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'fs_checkpoint_size', 'property_value': u'0.5'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'hadoop.security.authentication', 'property_value': u'simple'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'hadoop_heapsize', 'property_value': u'1024'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_
 name': u'hadoop_pid_dir_prefix', 'property_value': u'/var/run/hadoop'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'hdfs_log_dir_prefix', 'property_value': u'/var/log/hadoop'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'hdfs_user', 'property_value': u'hdfs'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'io.compression.codec.lzo.class', 'property_value': u'com.hadoop.compression.lzo.LzoCodec'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'io.compression.codecs', 'property_value': u'org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.hadoop.io.compress.SnappyCodec'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'io.file.buffer.size', 'property_value': u'131072'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'io.
 serializations', 'property_value': u'org.apache.hadoop.io.serializer.WritableSerialization'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'ipc.client.connect.max.retries', 'property_value': u'50'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'ipc.client.connection.maxidletime', 'property_value': u'30000'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'ipc.client.idlethreshold', 'property_value': u'8000'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'ipc.server.max.response.size', 'property_value': u'5242880'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'ipc.server.read.threadpool.size', 'property_value': u'5'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'kerberos_domain', 'property_value': u'EXAMPLE.COM'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'keytab_path', 'property_value': u'/etc/securit
 y/keytabs'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'namenode_formatted_mark_dir', 'property_value': u'/var/run/hadoop/hdfs/namenode/formatted/'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'namenode_heapsize', 'property_value': u'1024'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'namenode_opt_maxnewsize', 'property_value': u'640'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'namenode_opt_newsize', 'property_value': u'200'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'proxyuser_group', 'property_value': u'users'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.client.datanode.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.client.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name
 ': u'security.datanode.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.inter.datanode.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.inter.tracker.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.job.submission.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.namenode.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security.task.umbilical.protocol.acl', 'property_value': u'*'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'security_enabled', 'property_value': u'false'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'property_name': u'webinterface.private.actions', 'property_value': u'false'}]}
+    expected_first_item = StackConfigModel(None, property_name='datanode_du_reserved' , property_value='1' , service_name='HDFS' , stack_version='1.3.0')    
+    expected_request = None
+              
+    client = self.create_client()
+    configs = client.get_config('1.3.0','HDFS')
+    
+        
+    self.assertEquals(len(configs), 75)
+    self.assertEquals(str(configs[0]),str(expected_first_item))
+    self.assertEquals(configs.to_json_dict(), expected_dict)
+    
+  def test_get_components(self):
+    """
+    Test get components
+    """
+    expected_dict = {'items': [{'stack_version': u'1.3.0', 'service_name': u'HDFS', 'component_name': u'DATANODE'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'component_name': u'HDFS_CLIENT'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'component_name': u'NAMENODE'}, {'stack_version': u'1.3.0', 'service_name': u'HDFS', 'component_name': u'SECONDARY_NAMENODE'}]}
+    expected_first_item = StackComponentModel(None, component_name='DATANODE', service_name='HDFS' , stack_version='1.3.0')    
+    expected_request = None
+              
+    client = self.create_client()
+    components = client.get_components('1.3.0','HDFS')
+        
+    self.assertEquals(len(components), 4)
+    self.assertEquals(str(components[0]),str(expected_first_item))
+    self.assertEquals(components.to_json_dict(), expected_dict)
+  
\ No newline at end of file