فهرست منبع

AMBARI-3744. Integrate CustomServiceOrchestrator and new command format at the agent side

Lisnichenko Dmitro 11 سال پیش
والد
کامیت
44a6d2f702

+ 1 - 0
.gitignore

@@ -18,3 +18,4 @@ target
 .hgtags
 derby.log
 pass.txt
+ambari-agent/src/test/python/dummy_files/current-stack

+ 37 - 13
ambari-agent/src/main/python/ambari_agent/ActionQueue.py

@@ -31,6 +31,7 @@ import PuppetExecutor
 import PythonExecutor
 from ActualConfigHandler import ActualConfigHandler
 from CommandStatusDict import CommandStatusDict
+from CustomServiceOrchestrator import CustomServiceOrchestrator
 
 
 logger = logging.getLogger()
@@ -55,6 +56,9 @@ class ActionQueue(threading.Thread):
   COMPLETED_STATUS = 'COMPLETED'
   FAILED_STATUS = 'FAILED'
 
+  COMMAND_FORMAT_V1 = "1.0"
+  COMMAND_FORMAT_V2 = "2.0"
+
   def __init__(self, config, controller):
     super(ActionQueue, self).__init__()
     self.commandQueue = Queue.Queue()
@@ -65,6 +69,7 @@ class ActionQueue(threading.Thread):
     self.sh = shellRunner()
     self._stop = threading.Event()
     self.tmpdir = config.get('agent', 'prefix')
+    self.customServiceOrchestrator = CustomServiceOrchestrator(config)
 
   def stop(self):
     self._stop.set()
@@ -103,16 +108,33 @@ class ActionQueue(threading.Thread):
       logger.warn(err)
 
 
+  def determine_command_format_version(self, command):
+    """
+    Returns either COMMAND_FORMAT_V1 or COMMAND_FORMAT_V2
+    """
+    try:
+      if command['commandParams']['schema_version'] == self.COMMAND_FORMAT_V2:
+        return self.COMMAND_FORMAT_V2
+      else:
+        return  self.COMMAND_FORMAT_V1
+    except KeyError:
+      pass # ignore
+    return self.COMMAND_FORMAT_V1 # Fallback
+
+
   def execute_command(self, command):
     '''
     Executes commands of type  EXECUTION_COMMAND
     '''
     clusterName = command['clusterName']
     commandId = command['commandId']
+    command_format = self.determine_command_format_version(command)
 
-    logger.info("Executing command with id = " + str(commandId) +\
-                " for role = " + command['role'] + " of " +\
-                "cluster " + clusterName)
+    message = "Executing command with id = {commandId} for role = {role} of " \
+              "cluster {cluster}. Command format={command_format}".format(
+              commandId = str(commandId), role=command['role'],
+              cluster=clusterName, command_format=command_format)
+    logger.info(message)
     logger.debug(pprint.pformat(command))
 
     taskId = command['taskId']
@@ -124,17 +146,19 @@ class ActionQueue(threading.Thread):
       'status': self.IN_PROGRESS_STATUS
     })
     self.commandStatuses.put_command_status(command, in_progress_status)
-    # TODO: Add CustomServiceOrchestrator call somewhere here
     # running command
-    # Create a new instance of executor for the current thread
-    puppetExecutor = PuppetExecutor.PuppetExecutor(
-      self.config.get('puppet', 'puppetmodules'),
-      self.config.get('puppet', 'puppet_home'),
-      self.config.get('puppet', 'facter_home'),
-      self.config.get('agent', 'prefix'), self.config)
-    commandresult = puppetExecutor.runCommand(command, in_progress_status['tmpout'],
-      in_progress_status['tmperr'])
-
+    if command_format == self.COMMAND_FORMAT_V1:
+      # Create a new instance of executor for the current thread
+      puppetExecutor = PuppetExecutor.PuppetExecutor(
+        self.config.get('puppet', 'puppetmodules'),
+        self.config.get('puppet', 'puppet_home'),
+        self.config.get('puppet', 'facter_home'),
+        self.config.get('agent', 'prefix'), self.config)
+      commandresult = puppetExecutor.runCommand(command, in_progress_status['tmpout'],
+        in_progress_status['tmperr'])
+    else:
+      commandresult = self.customServiceOrchestrator.runCommand(command,
+        in_progress_status['tmpout'], in_progress_status['tmperr'])
     # dumping results
     status = self.COMPLETED_STATUS
     if commandresult['exitcode'] != 0:

+ 13 - 41
ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py

@@ -48,19 +48,18 @@ class CustomServiceOrchestrator():
 
   def runCommand(self, command, tmpoutfile, tmperrfile):
     try:
-      # TODO: Adjust variables
-      service_name = command['serviceName']
       component_name = command['role']
-      stack_name = command['stackName'] # TODO: add at the server side
-      stack_version = command['stackVersion'] # TODO: add at the server side
-      script_type = command['scriptType'] # TODO: add at the server side
-      script = command['script']
+      stack_name = command['hostLevelParams']['stack_name']
+      stack_version = command['hostLevelParams']['stack_version']
+      script_type = command['commandParams']['script_type']
+      script = command['commandParams']['script']
       command_name = command['roleCommand']
-      timeout = int(command['timeout']) # TODO: add at the server side
+      timeout = int(command['commandParams']['command_timeout'])
+      metadata_folder = command['commandParams']['service_metadata_folder']
       base_dir = self.file_cache.get_service_base_dir(
-          stack_name, stack_version, service_name, component_name)
+          stack_name, stack_version, metadata_folder, component_name)
       script_path = self.resolve_script_path(base_dir, script, script_type)
-      if script_type == self.SCRIPT_TYPE_PYTHON:
+      if script_type.upper() == self.SCRIPT_TYPE_PYTHON:
         json_path = self.dump_command_to_json(command)
         script_params = [command_name, json_path, base_dir]
         ret = self.python_executor.run_file(
@@ -76,7 +75,7 @@ class CustomServiceOrchestrator():
       ret = {
         'stdout' : message,
         'stderr' : message,
-        'exitCode': 1,
+        'exitcode': 1,
       }
     return ret
 
@@ -96,37 +95,10 @@ class CustomServiceOrchestrator():
     """
     Converts command to json file and returns file path
     """
-    command_id = command['commandId']
-    file_path = os.path.join(self.tmp_dir, "command-{0}.json".format(command_id))
-    with open(file_path, "w") as f:
+    task_id = command['taskId']
+    file_path = os.path.join(self.tmp_dir, "command-{0}.json".format(task_id))
+    # Command json contains passwords, that's why we need proper permissions
+    with os.fdopen(os.open(file_path, os.O_WRONLY | os.O_CREAT,0600), 'w') as f:
       content = json.dumps(command)
       f.write(content)
     return file_path
-
-
-def main():
-  """
-  May be used for manual testing if needed
-  """
-  config = AmbariConfig().getConfig()
-  orchestrator = CustomServiceOrchestrator(config)
-  config.set('agent', 'prefix', "/tmp")
-  command = {
-    "serviceName" : "HBASE",
-    "role" : "HBASE_MASTER",
-    "stackName" : "HDP",
-    "stackVersion" : "1.2.0",
-    "scriptType" : "PYTHON",
-    "script" : "/tmp/1.py",
-    "roleCommand" : "START",
-    "timeout": 600
-  }
-
-  result = orchestrator.runCommand(command, "/tmp/out-1.txt", "/tmp/err-1.txt")
-  pprint.pprint(result)
-  pass
-
-
-
-if __name__ == "__main__":
-  main()

+ 1 - 5
ambari-agent/src/main/python/ambari_agent/FileCache.py

@@ -19,16 +19,12 @@ limitations under the License.
 '''
 
 
-# TODO: Update class description
-
-
 import logging
 import Queue
 import threading
 import pprint
 import os
 import json
-from ServiceComponentMetadata import ServiceComponentMetadata
 from AgentException import AgentException
 
 logger = logging.getLogger()
@@ -50,7 +46,7 @@ class FileCache():
     Returns a base directory for service
     """
     metadata_path = os.path.join(self.cache_dir, "stacks", str(stack_name),
-                                 str(stack_version), str(service))
+                                 str(stack_version), "services", str(service))
     if not os.path.isdir(metadata_path):
       # TODO: Metadata downloading will be implemented at Phase 2
       # As of now, all stack definitions are packaged and distributed with

+ 57 - 0
ambari-agent/src/test/python/TestActionQueue.py

@@ -31,6 +31,7 @@ from threading import Thread
 
 from mock.mock import patch, MagicMock, call
 from ambari_agent.StackVersionsFileHandler import StackVersionsFileHandler
+from ambari_agent.CustomServiceOrchestrator import CustomServiceOrchestrator
 
 
 class TestActionQueue(TestCase):
@@ -361,3 +362,59 @@ class TestActionQueue(TestCase):
     expected = 'dummy report'
     self.assertEqual(len(report['componentStatus']), 1)
     self.assertEqual(report['componentStatus'][0], expected)
+
+
+  def test_determine_command_format_version(self):
+    v1_command = {
+      'commandParams': {
+        'schema_version': '1.0'
+      }
+    }
+    v2_command = {
+      'commandParams': {
+        'schema_version': '2.0'
+      }
+    }
+    current_command = {
+      # Absent 'commandParams' section
+    }
+    actionQueue = ActionQueue(AmbariConfig().getConfig(), 'dummy_controller')
+    self.assertEqual(actionQueue.determine_command_format_version(v1_command),
+                     ActionQueue.COMMAND_FORMAT_V1)
+    self.assertEqual(actionQueue.determine_command_format_version(v2_command),
+                     ActionQueue.COMMAND_FORMAT_V2)
+    self.assertEqual(actionQueue.determine_command_format_version(current_command),
+                     ActionQueue.COMMAND_FORMAT_V1)
+
+
+  @patch.object(ActionQueue, "determine_command_format_version")
+  @patch("__builtin__.open")
+  @patch.object(PuppetExecutor, "runCommand")
+  @patch.object(CustomServiceOrchestrator, "runCommand")
+  @patch.object(ActionQueue, "status_update_callback")
+  def test_command_execution_depending_on_command_format(self,
+                                status_update_callback_mock,
+                                custom_ex_runCommand_mock,
+                                puppet_runCommand_mock, open_mock,
+                                determine_command_format_version_mock):
+    actionQueue = ActionQueue(AmbariConfig().getConfig(), 'dummy_controller')
+    ret = {
+      'stdout' : '',
+      'stderr' : '',
+      'exitcode': 1,
+      }
+    puppet_runCommand_mock.return_value = ret
+    determine_command_format_version_mock.return_value = \
+                                  ActionQueue.COMMAND_FORMAT_V1
+    actionQueue.execute_command(self.datanode_install_command)
+    self.assertTrue(puppet_runCommand_mock.called)
+    self.assertFalse(custom_ex_runCommand_mock.called)
+
+    puppet_runCommand_mock.reset_mock()
+
+    custom_ex_runCommand_mock.return_value = ret
+    determine_command_format_version_mock.return_value = \
+      ActionQueue.COMMAND_FORMAT_V2
+    actionQueue.execute_command(self.datanode_install_command)
+    self.assertFalse(puppet_runCommand_mock.called)
+    self.assertTrue(custom_ex_runCommand_mock.called)

+ 93 - 3
ambari-agent/src/test/python/TestCustomServiceOrchestrator.py

@@ -18,6 +18,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 import ConfigParser
+import os
 
 import pprint
 
@@ -28,10 +29,13 @@ import time
 from threading import Thread
 
 from PythonExecutor import PythonExecutor
+from CustomServiceOrchestrator import CustomServiceOrchestrator
 from AmbariConfig import AmbariConfig
 from mock.mock import MagicMock, patch
 import StringIO
 import sys
+from AgentException import AgentException
+from FileCache import FileCache
 
 
 class TestCustomServiceOrchestrator(TestCase):
@@ -42,9 +46,95 @@ class TestCustomServiceOrchestrator(TestCase):
     sys.stdout = out
     # generate sample config
     tmpdir = tempfile.gettempdir()
-    config = ConfigParser.RawConfigParser()
-    config.add_section('agent')
-    config.set('agent', 'prefix', tmpdir)
+    self.config = ConfigParser.RawConfigParser()
+    self.config.add_section('agent')
+    self.config.set('agent', 'prefix', tmpdir)
+    self.config.set('agent', 'cache_dir', "/cachedir")
+
+
+  def test_dump_command_to_json(self):
+    command = {
+      'commandType': 'EXECUTION_COMMAND',
+      'role': u'DATANODE',
+      'roleCommand': u'INSTALL',
+      'commandId': '1-1',
+      'taskId': 3,
+      'clusterName': u'cc',
+      'serviceName': u'HDFS',
+      'configurations':{'global' : {}},
+      'configurationTags':{'global' : { 'tag': 'v1' }}
+    }
+    config = AmbariConfig().getConfig()
+    tempdir = tempfile.gettempdir()
+    config.set('agent', 'prefix', tempdir)
+    orchestrator = CustomServiceOrchestrator(config)
+    file = orchestrator.dump_command_to_json(command)
+    self.assertTrue(os.path.exists(file))
+    self.assertTrue(os.path.getsize(file) > 0)
+    self.assertEqual(oct(os.stat(file).st_mode & 0777), '0600')
+    os.unlink(file)
+
+
+  @patch("os.path.exists")
+  def test_resolve_script_path(self, exists_mock):
+    config = AmbariConfig().getConfig()
+    orchestrator = CustomServiceOrchestrator(config)
+    # Testing existing path
+    exists_mock.return_value = True
+    path = orchestrator.\
+      resolve_script_path("/HBASE", "scripts/hbase_master.py", "PYTHON")
+    self.assertEqual("/HBASE/package/scripts/hbase_master.py", path)
+    # Testing not existing path
+    exists_mock.return_value = False
+    try:
+      orchestrator.resolve_script_path("/HBASE",
+                                       "scripts/hbase_master.py", "PYTHON")
+      self.fail('ExpectedException not thrown')
+    except AgentException:
+      pass # Expected
+
+
+  @patch.object(CustomServiceOrchestrator, "resolve_script_path")
+  @patch.object(FileCache, "get_service_base_dir")
+  @patch.object(CustomServiceOrchestrator, "dump_command_to_json")
+  @patch.object(PythonExecutor, "run_file")
+  def test_runCommand(self, run_file_mock, dump_command_to_json_mock,
+                      get_service_base_dir_mock, resolve_script_path_mock):
+    command = {
+      'role' : 'REGION_SERVER',
+      'hostLevelParams' : {
+        'stack_name' : 'HDP',
+        'stack_version' : '2.0.7',
+      },
+      'commandParams': {
+        'script_type': 'PYTHON',
+        'script': 'scripts/hbase_regionserver.py',
+        'command_timeout': '600',
+        'service_metadata_folder' : 'HBASE'
+      },
+      'roleCommand': 'INSTALL'
+    }
+    get_service_base_dir_mock.return_value = "/basedir/"
+    resolve_script_path_mock.return_value = "/basedir/scriptpath"
+    orchestrator = CustomServiceOrchestrator(self.config)
+    # normal run case
+    run_file_mock.return_value = {
+        'stdout' : 'sss',
+        'stderr' : 'eee',
+        'exitcode': 0,
+      }
+    ret = orchestrator.runCommand(command, "out.txt", "err.txt")
+    self.assertEqual(ret['exitcode'], 0)
+    self.assertTrue(run_file_mock.called)
+
+    run_file_mock.reset_mock()
+    # unknown script type case
+    command['commandParams']['script_type'] = "PUPPET"
+    ret = orchestrator.runCommand(command, "out.txt", "err.txt")
+    self.assertEqual(ret['exitcode'], 1)
+    self.assertFalse(run_file_mock.called)
+    self.assertTrue("Unknown script type" in ret['stdout'])
+    pass
 
 
   def tearDown(self):

+ 68 - 0
ambari-agent/src/test/python/TestFileCache.py

@@ -0,0 +1,68 @@
+#!/usr/bin/env python2.6
+
+'''
+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 ConfigParser
+import os
+
+import pprint
+
+from unittest import TestCase
+import threading
+import tempfile
+import time
+from threading import Thread
+
+from PythonExecutor import PythonExecutor
+from CustomServiceOrchestrator import CustomServiceOrchestrator
+from FileCache import FileCache
+from AmbariConfig import AmbariConfig
+from mock.mock import MagicMock, patch
+import StringIO
+import sys
+from ambari_agent import AgentException
+
+
+class TestFileCache(TestCase):
+
+  def setUp(self):
+    # disable stdout
+    out = StringIO.StringIO()
+    sys.stdout = out
+    # generate sample config
+    tmpdir = tempfile.gettempdir()
+    self.config = ConfigParser.RawConfigParser()
+    self.config.add_section('agent')
+    self.config.set('agent', 'prefix', tmpdir)
+    self.config.set('agent', 'cache_dir', "/var/lib/ambari-agent/cache")
+
+
+  @patch("os.path.isdir")
+  def test_get_service_base_dir(self, isdir_mock):
+    fileCache = FileCache(self.config)
+    isdir_mock.return_value = True
+    base = fileCache.get_service_base_dir("HDP", "2.0.7",
+                                          "HBASE", "REGION_SERVER")
+    self.assertEqual(base, "/var/lib/ambari-agent/cache/stacks/HDP/2.0.7/services/HBASE")
+
+
+  def tearDown(self):
+    # enable stdout
+    sys.stdout = sys.__stdout__
+
+

+ 1 - 0
ambari-agent/src/test/python/TestMain.py

@@ -94,6 +94,7 @@ class TestMain(unittest.TestCase):
 
     # Testing with default setup (config file does not contain loglevel entry)
     # Log level should not be changed
+    config.set('agent', 'loglevel', None)
     main.update_log_level(config)
     self.assertFalse(setLevel_mock.called)
 

+ 91 - 0
ambari-server/src/main/resources/stacks/HDP/2.0._/services/HBASE/metainfo.xml

@@ -0,0 +1,91 @@
+<?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.
+-->
+<metainfo>
+  <schemaVersion>2.0</schemaVersion>
+  <services>
+    <service>
+      <name>HBASE</name>
+      <comment>Non-relational distributed database and centralized service for configuration management &amp;
+        synchronization
+      </comment>
+      <version>0.96.0.2.0.6.0</version>
+      <components>
+        <component>
+          <name>HBASE_MASTER</name>
+          <category>MASTER</category>
+          <commandScript>
+            <script>scripts/hbase_master.py</script>
+            <scriptType>PYTHON</scriptType>
+            <timeout>600</timeout>
+          </commandScript>
+        </component>
+
+        <component>
+          <name>HBASE_REGIONSERVER</name>
+          <category>SLAVE</category>
+          <commandScript>
+            <script>scripts/hbase_regionserver.py</script>
+            <scriptType>PYTHON</scriptType>
+          </commandScript>
+          <customCommands>
+            <customCommand>
+              <name>DECOMMISSION_REGIONSERVER</name>
+              <commandScript>
+                <script>scripts/hbase_regionserver.py</script>
+                <scriptType>PYTHON</scriptType>
+                <timeout>600</timeout>
+              </commandScript>
+            </customCommand>
+          </customCommands>
+        </component>
+
+        <component>
+          <name>HBASE_CLIENT</name>
+          <category>CLIENT</category>
+        </component>
+        <commandScript>
+          <script>scripts/hbase_client.py</script>
+          <scriptType>PYTHON</scriptType>
+        </commandScript>
+      </components>
+
+      <osSpecificPackages>
+        <osSpecificPackage>
+          <osType>centos6</osType>
+          <packages>
+            <package>
+              <type>rpm</type>
+              <name>hbase</name>
+            </package>
+          </packages>
+        </osSpecificPackage>
+      </osSpecificPackages>
+
+      <customCommands>
+        <customCommand>
+          <name>SERVICE_CHECK</name>
+          <commandScript>
+            <script>scripts/hbase_service.py</script>
+            <scriptType>PYTHON</scriptType>
+            <timeout>300</timeout>
+          </commandScript>
+        </customCommand>
+      </customCommands>
+    </service>
+  </services>
+</metainfo>

+ 38 - 0
ambari-server/src/main/resources/stacks/HDP/2.0._/services/HBASE/package/scripts/hbase_client.py

@@ -0,0 +1,38 @@
+#!/usr/bin/env python2.6
+
+'''
+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 resource_management import *
+
+class Client(Script):
+
+  def install(self):
+    print "Install!"
+
+  def start(self):
+    print "Start!"
+
+  def stop(self):
+    print "Stop!"
+
+  def configure(self):
+    print "Configure!"
+
+if __name__ == "__main__":
+  Client().execute()

+ 38 - 0
ambari-server/src/main/resources/stacks/HDP/2.0._/services/HBASE/package/scripts/hbase_master.py

@@ -0,0 +1,38 @@
+#!/usr/bin/env python2.6
+
+'''
+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 resource_management import *
+
+class Master(Script):
+
+  def install(self):
+    print "Install!"
+
+  def start(self):
+    print "Start!"
+
+  def stop(self):
+    print "Stop!"
+
+  def configure(self):
+    print "Configure!"
+
+if __name__ == "__main__":
+  Master().execute()

+ 38 - 0
ambari-server/src/main/resources/stacks/HDP/2.0._/services/HBASE/package/scripts/hbase_regionserver.py

@@ -0,0 +1,38 @@
+#!/usr/bin/env python2.6
+
+'''
+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 resource_management import *
+
+class RegionServer(Script):
+
+  def install(self):
+    print "Install!"
+
+  def start(self):
+    print "Start!"
+
+  def stop(self):
+    print "Stop!"
+
+  def configure(self):
+    print "Configure!"
+
+if __name__ == "__main__":
+  RegionServer().execute()

+ 38 - 0
ambari-server/src/main/resources/stacks/HDP/2.0._/services/HBASE/package/scripts/hbase_service.py

@@ -0,0 +1,38 @@
+#!/usr/bin/env python2.6
+
+'''
+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 resource_management import *
+
+class Service(Script):
+
+  def install(self):
+    print "Install!"
+
+  def start(self):
+    print "Start!"
+
+  def stop(self):
+    print "Stop!"
+
+  def configure(self):
+    print "Configure!"
+
+if __name__ == "__main__":
+  Service().execute()