You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sm...@apache.org on 2014/01/22 20:26:23 UTC

[2/2] git commit: AMBARI-4365. Action definitions should be provided as declarative resources - read from XML files

AMBARI-4365. Action definitions should be provided as declarative resources - read from XML files


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/8e8a9a47
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/8e8a9a47
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/8e8a9a47

Branch: refs/heads/trunk
Commit: 8e8a9a47fd5d9afb0fcc4bd41b283c409d5bc355
Parents: 241f059
Author: Sumit Mohanty <sm...@hortonworks.com>
Authored: Wed Jan 22 09:42:45 2014 -0800
Committer: Sumit Mohanty <sm...@hortonworks.com>
Committed: Wed Jan 22 11:14:09 2014 -0800

----------------------------------------------------------------------
 ambari-server/conf/unix/ambari.properties       |   1 +
 ambari-server/pom.xml                           |   8 +
 .../server/actionmanager/ActionDefinition.java  | 138 -----------
 .../server/actionmanager/ActionManager.java     |  35 +--
 .../actionmanager/CustomActionDBAccessor.java   |  82 -------
 .../CustomActionDBAccessorImpl.java             | 230 -------------------
 .../server/api/services/AmbariMetaInfo.java     |  78 +++++--
 .../server/configuration/Configuration.java     | 165 ++++++-------
 .../server/controller/ActionResponse.java       |  11 -
 .../controller/AmbariActionExecutionHelper.java |  19 +-
 .../server/controller/ControllerModule.java     |  62 +++--
 .../internal/ActionResourceProvider.java        | 144 +-----------
 .../server/customactions/ActionDefinition.java  | 132 +++++++++++
 .../customactions/ActionDefinitionManager.java  | 202 ++++++++++++++++
 .../customactions/ActionDefinitionSpec.java     | 139 +++++++++++
 .../customactions/ActionDefinitionXml.java      |  42 ++++
 .../server/orm/dao/ActionDefinitionDAO.java     |  71 ------
 .../server/orm/entities/ActionEntity.java       | 162 -------------
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   1 -
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   1 -
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   7 -
 .../system_action_definitions.xml               |  32 +++
 .../custom_actions/ambari_hdfs_rebalance.py     |  58 -----
 .../custom_actions/ambari_hdfs_rebalancer.py    |  58 +++++
 .../upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql   |   1 -
 .../ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql   |   5 -
 .../actionmanager/TestActionDBAccessorImpl.java |   4 +-
 .../server/actionmanager/TestActionManager.java |   6 +-
 .../actionmanager/TestActionScheduler.java      |  10 +-
 .../TestCustomActionDBAccessorImpl.java         | 124 ----------
 .../server/agent/TestHeartbeatHandler.java      |   5 +-
 .../AmbariManagementControllerTest.java         | 160 ++-----------
 .../internal/ActionResourceProviderTest.java    | 213 ++---------------
 .../ActionDefinitionManagerTest.java            |  73 ++++++
 .../server/orm/dao/ActionDefinitionDAOTest.java | 137 -----------
 .../cust_action_definitions1.xml                |  55 +++++
 .../cust_action_definitions2.txt                |  18 ++
 .../cust_action_definitions3.xml                |  21 ++
 .../system_action_definitions.xml               |  32 +++
 39 files changed, 1057 insertions(+), 1685 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/conf/unix/ambari.properties
----------------------------------------------------------------------
diff --git a/ambari-server/conf/unix/ambari.properties b/ambari-server/conf/unix/ambari.properties
index 978cdc6..74eed1b 100644
--- a/ambari-server/conf/unix/ambari.properties
+++ b/ambari-server/conf/unix/ambari.properties
@@ -18,6 +18,7 @@
 
 security.server.keys_dir = /var/lib/ambari-server/keys
 resources.dir = /var/lib/ambari-server/resources
+custom.action.definitions = /var/lib/ambari-server/resources/custom_action_definitions
 jdk1.6.url=http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-6u31-linux-x64.bin
 jce_policy1.6.url=http://public-repo-1.hortonworks.com/ARTIFACTS/jce_policy-6.zip
 jdk1.7.url=http://www.reucon.com/cdn/java/jdk-7u45-linux-x64.tar.gz

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index 2972e94..b494b7b 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -426,6 +426,14 @@
                 </source>
               </sources>
             </mapping>
+            <mapping>
+              <directory>/var/lib/ambari-server/resources/custom_action_definitions</directory>
+              <sources>
+                <source>
+                  <location>src/main/resources/custom_action_definitions</location>
+                </source>
+              </sources>
+            </mapping>
           </mappings>
         </configuration>
       </plugin>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDefinition.java
deleted file mode 100644
index fdd57dd..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDefinition.java
+++ /dev/null
@@ -1,138 +0,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.
- */
-
-package org.apache.ambari.server.actionmanager;
-
-import org.apache.ambari.server.orm.entities.ActionEntity;
-
-/**
- * The resource describing the definition of an action
- */
-public class ActionDefinition {
-  private String actionName;
-  private ActionType actionType;
-  private String inputs;
-  private String targetService;
-  private String targetComponent;
-  private String description;
-  private TargetHostType targetType;
-  private Short defaultTimeout;
-
-  /**
-   * Create an instance of ActionDefinition
-   * @param actionName      The name of the action
-   * @param actionType      The type fo the action
-   * @param inputs          Expected input of the action
-   * @param targetService   Target service type (e.g. HDFS)
-   * @param targetComponent Target component type (e.g. DATANODE)
-   * @param description     Short description of the action
-   * @param targetType      Selection criteria for target hosts
-   * @param defaultTimeout  The timeout value for this action when executed
-   */
-  public ActionDefinition(String actionName, ActionType actionType, String inputs,
-                          String targetService, String targetComponent, String description,
-                          TargetHostType targetType, Short defaultTimeout) {
-    setActionName(actionName);
-    setActionType(actionType);
-    setInputs(inputs);
-    setTargetService(targetService);
-    setTargetComponent(targetComponent);
-    setDescription(description);
-    setTargetType(targetType);
-    setDefaultTimeout(defaultTimeout);
-  }
-
-  /**
-   * Create an instance of ActionDefinition
-   * @param entity  The entity corresponding to the action
-   */
-  public ActionDefinition(ActionEntity entity) {
-    setActionName(entity.getActionName());
-    setActionType(entity.getActionType());
-    setInputs(entity.getInputs());
-    setTargetService(entity.getTargetService());
-    setTargetComponent(entity.getTargetComponent());
-    setDescription(entity.getDescription());
-    setTargetType(entity.getTargetType());
-    setDefaultTimeout(entity.getDefaultTimeout());
-  }
-
-  public String getActionName() {
-    return actionName;
-  }
-
-  public void setActionName(String actionName) {
-    this.actionName = actionName;
-  }
-
-  public ActionType getActionType() {
-    return actionType;
-  }
-
-  public void setActionType(ActionType actionType) {
-    this.actionType = actionType;
-  }
-
-  public String getInputs() {
-    return inputs;
-  }
-
-  public void setInputs(String inputs) {
-    this.inputs = inputs;
-  }
-
-  public String getTargetService() {
-    return targetService;
-  }
-
-  public void setTargetService(String targetService) {
-    this.targetService = targetService;
-  }
-
-  public String getTargetComponent() {
-    return targetComponent;
-  }
-
-  public void setTargetComponent(String targetComponent) {
-    this.targetComponent = targetComponent;
-  }
-
-  public String getDescription() {
-    return description;
-  }
-
-  public void setDescription(String description) {
-    this.description = description;
-  }
-
-  public TargetHostType getTargetType() {
-    return targetType;
-  }
-
-  public void setTargetType(TargetHostType targetType) {
-    this.targetType = targetType;
-  }
-
-  public Short getDefaultTimeout() {
-    return defaultTimeout;
-  }
-
-  public void setDefaultTimeout(Short defaultTimeout) {
-    this.defaultTimeout = defaultTimeout;
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java
index 8e6fb13..3788d75 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionManager.java
@@ -50,7 +50,6 @@ public class ActionManager {
   private final ActionDBAccessor db;
   private final ActionQueue actionQueue;
   private final AtomicLong requestCounter;
-  private final CustomActionDBAccessor cdb;
   private final RequestFactory requestFactory;
 
 
@@ -58,7 +57,7 @@ public class ActionManager {
   public ActionManager(@Named("schedulerSleeptime") long schedulerSleepTime,
                        @Named("actionTimeout") long actionTimeout,
                        ActionQueue aq, Clusters fsm, ActionDBAccessor db, HostsMap hostsMap,
-                       ServerActionManager serverActionManager, UnitOfWork unitOfWork, CustomActionDBAccessor cdb,
+                       ServerActionManager serverActionManager, UnitOfWork unitOfWork,
                        RequestFactory requestFactory, Configuration configuration) {
     this.actionQueue = aq;
     this.db = db;
@@ -66,7 +65,6 @@ public class ActionManager {
         actionQueue, fsm, 2, hostsMap, serverActionManager, unitOfWork, configuration);
     requestCounter = new AtomicLong(
         db.getLastPersistedRequestIdWhenInitialized());
-    this.cdb = cdb;
     this.requestFactory = requestFactory;
   }
 
@@ -207,35 +205,4 @@ public class ActionManager {
   public String getRequestContext(long requestId) {
     return db.getRequestContext(requestId);
   }
-
-  /** CRUD operations of Action resources **/
-
-  public ActionDefinition getActionDefinition(String actionName)
-      throws AmbariException {
-    return cdb.getActionDefinition(actionName);
-  }
-
-  public List<ActionDefinition> getAllActionDefinition()
-      throws AmbariException {
-    return cdb.getActionDefinitions();
-  }
-
-  public void deleteActionDefinition(String actionName)
-      throws AmbariException {
-    cdb.deleteActionDefinition(actionName);
-  }
-
-  public void updateActionDefinition(String actionName, ActionType actionType, String description,
-                                     TargetHostType targetType, Short defaultTimeout)
-      throws AmbariException {
-    cdb.updateActionDefinition(actionName, actionType, description, targetType, defaultTimeout);
-  }
-
-  public void createActionDefinition(String actionName, ActionType actionType, String inputs, String description,
-                                     String serviceType, String componentType, TargetHostType targetType,
-                                     Short defaultTimeout)
-      throws AmbariException {
-    cdb.createActionDefinition(actionName, actionType, inputs, description, targetType, serviceType,
-        componentType, defaultTimeout);
-  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessor.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessor.java
deleted file mode 100644
index b861e76..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessor.java
+++ /dev/null
@@ -1,82 +0,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.
- */
-package org.apache.ambari.server.actionmanager;
-
-import org.apache.ambari.server.AmbariException;
-
-import java.util.List;
-
-/**
- * The DB accessor implementation for Action definitions
- */
-public interface CustomActionDBAccessor {
-
-  /**
-   * Given an actionName, get the Action resource
-   *
-   * @param actionName name of the action
-   * @return
-   * @throws AmbariException
-   */
-  public ActionDefinition getActionDefinition(String actionName) throws AmbariException;
-
-  /**
-   * Get all action definition resources
-   *
-   * @return
-   */
-  public List<ActionDefinition> getActionDefinitions();
-
-  /**
-   * Create an action definition resource
-   *
-   * @param actionName     name of the action
-   * @param actionType     type of the action
-   * @param inputs         inputs required by the action
-   * @param description    a short description of the action
-   * @param targetType     the host target type
-   * @param serviceType    the service type on which the action must be executed
-   * @param componentType  the component type on which the action must be executed
-   * @param defaultTimeout the default timeout for this action
-   * @throws AmbariException
-   */
-  public void createActionDefinition(String actionName, ActionType actionType, String inputs, String description,
-                                     TargetHostType targetType, String serviceType, String componentType,
-                                     Short defaultTimeout) throws AmbariException;
-
-  /**
-   * Update an action definition
-   *
-   * @param actionName     name of the action
-   * @param actionType     type of the action
-   * @param description    a short description of the action
-   * @param targetType     the host target type
-   * @param defaultTimeout the default timeout for this action
-   * @throws AmbariException
-   */
-  public void updateActionDefinition(String actionName, ActionType actionType, String description,
-                                     TargetHostType targetType, Short defaultTimeout) throws AmbariException;
-
-  /**
-   * Delete an action definition
-   *
-   * @param actionName
-   * @throws AmbariException
-   */
-  public void deleteActionDefinition(String actionName) throws AmbariException;
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessorImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessorImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessorImpl.java
deleted file mode 100644
index 38bc371..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessorImpl.java
+++ /dev/null
@@ -1,230 +0,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.
- */
-package org.apache.ambari.server.actionmanager;
-
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.orm.dao.ActionDefinitionDAO;
-import org.apache.ambari.server.orm.entities.ActionEntity;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An implementation of DB accessor for Custom Action
- */
-@Singleton
-public class CustomActionDBAccessorImpl implements CustomActionDBAccessor {
-  public static final Short MIN_TIMEOUT = 60;
-  private static final Logger LOG = LoggerFactory.getLogger(CustomActionDBAccessorImpl.class);
-  private static final Short MAX_TIMEOUT = 600;
-  @Inject
-  private ActionDefinitionDAO actionDefinitionDAO;
-
-  @Inject
-  public CustomActionDBAccessorImpl(Injector injector) {
-    injector.injectMembers(this);
-  }
-
-  /**
-   * Given an actionName, get the Action resource
-   *
-   * @param actionName name of the action
-   * @return
-   * @throws AmbariException
-   */
-  @Override
-  public ActionDefinition getActionDefinition(String actionName) {
-    ActionEntity action =
-        actionDefinitionDAO.findByPK(actionName);
-    if (action != null) {
-      return new ActionDefinition(action);
-    }
-
-    return null;
-  }
-
-  /**
-   * Get all action definition resources
-   *
-   * @return
-   */
-  @Override
-  public List<ActionDefinition> getActionDefinitions() {
-    List<ActionDefinition> result = new ArrayList<ActionDefinition>();
-    List<ActionEntity> entities = actionDefinitionDAO.findAll();
-    for (ActionEntity entity : entities) {
-      result.add(new ActionDefinition(entity));
-    }
-    return result;
-  }
-
-  /**
-   * Create an action definition resource
-   *
-   * @param actionName     name of the action
-   * @param actionType     type of the action
-   * @param inputs         inputs required by the action
-   * @param description    a short description of the action
-   * @param targetType     the host target type
-   * @param serviceType    the service type on which the action must be executed
-   * @param componentType  the component type on which the action must be executed
-   * @param defaultTimeout the default timeout for this action
-   * @throws AmbariException
-   */
-  @Override
-  @Transactional
-  public void createActionDefinition(String actionName, ActionType actionType, String inputs, String description,
-                                     TargetHostType targetType, String serviceType, String componentType,
-                                     Short defaultTimeout)
-      throws AmbariException {
-    validateCreateInput(actionName, actionType, inputs, description, defaultTimeout,
-        targetType, serviceType, componentType);
-    ActionEntity entity =
-        actionDefinitionDAO.findByPK(actionName);
-    if (entity == null) {
-      entity = new ActionEntity();
-      entity.setActionName(actionName);
-      entity.setActionType(actionType);
-      entity.setInputs(inputs);
-      entity.setTargetService(serviceType);
-      entity.setTargetComponent(componentType);
-      entity.setDescription(description);
-      entity.setTargetType(targetType);
-      entity.setDefaultTimeout(defaultTimeout);
-      actionDefinitionDAO.merge(entity);
-    } else {
-      throw new AmbariException("Action definition " + actionName + " already exists");
-    }
-  }
-
-  /**
-   * Update an action definition
-   *
-   * @param actionName     name of the action
-   * @param actionType     type of the action
-   * @param description    a short description of the action
-   * @param targetType     the host target type
-   * @param defaultTimeout the default timeout for this action
-   * @throws AmbariException
-   */
-  @Override
-  @Transactional
-  public void updateActionDefinition(String actionName, ActionType actionType, String description,
-                                     TargetHostType targetType, Short defaultTimeout)
-      throws AmbariException {
-    ActionEntity entity = actionDefinitionDAO.findByPK(actionName);
-    if (entity != null) {
-      if (actionType != null) {
-        if (actionType == ActionType.SYSTEM_DISABLED) {
-          throw new AmbariException("Action type cannot be " + actionType);
-        }
-        entity.setActionType(actionType);
-      }
-      if (description != null) {
-        if (description.isEmpty()) {
-          throw new AmbariException("Action description cannot be empty");
-        }
-        entity.setDescription(description);
-      }
-      if (targetType != null) {
-        entity.setTargetType(targetType);
-      }
-      if (defaultTimeout != null) {
-        if (defaultTimeout < MIN_TIMEOUT || defaultTimeout > MAX_TIMEOUT) {
-          throw new AmbariException("Default timeout should be between " + MIN_TIMEOUT + " and " + MAX_TIMEOUT);
-        }
-        entity.setDefaultTimeout(defaultTimeout);
-      }
-      actionDefinitionDAO.merge(entity);
-    } else {
-      throw new AmbariException("Action definition " + actionName + " does not exist");
-    }
-  }
-
-  /**
-   * Delete an action definition
-   *
-   * @param actionName
-   * @throws AmbariException
-   */
-  @Override
-  public void deleteActionDefinition(String actionName)
-      throws AmbariException {
-    validateActionName(actionName);
-    ActionDefinition ad = getActionDefinition(actionName);
-    if (ad != null) {
-      actionDefinitionDAO.removeByPK(actionName);
-    }
-  }
-
-  private void validateCreateInput(String actionName, ActionType actionType, String inputs,
-                                   String description, Short defaultTimeout,
-                                   TargetHostType targetType, String serviceType, String componentType)
-      throws AmbariException {
-
-    validateActionName(actionName);
-
-    if (defaultTimeout < MIN_TIMEOUT || defaultTimeout > MAX_TIMEOUT) {
-      throw new AmbariException("Default timeout should be between " + MIN_TIMEOUT + " and " + MAX_TIMEOUT);
-    }
-
-    if (actionType == ActionType.SYSTEM_DISABLED) {
-      throw new AmbariException("Action type cannot be " + actionType);
-    }
-
-    if (description == null || description.isEmpty()) {
-      throw new AmbariException("Action description cannot be empty");
-    }
-
-    if (actionType == null || actionType == ActionType.SYSTEM_DISABLED) {
-      throw new AmbariException("Action type cannot be " + actionType);
-    }
-
-    if (serviceType == null || serviceType.isEmpty()) {
-      if (componentType != null && !componentType.isEmpty()) {
-        throw new AmbariException("Target component cannot be specified unless target service is specified");
-      }
-    }
-
-    if (inputs != null && !inputs.isEmpty()) {
-      String[] parameters = inputs.split(",");
-      for (String parameter : parameters) {
-        if (parameter.trim().isEmpty()) {
-          throw new AmbariException("Empty parameter cannot be specified as an input parameter");
-        }
-      }
-    }
-  }
-
-  private void validateActionName(String actionName)
-      throws AmbariException {
-    if (actionName == null || actionName.isEmpty()) {
-      throw new AmbariException("Action name cannot be empty");
-    }
-    String trimmedName = actionName.replaceAll("\\s+", "");
-    if (actionName.length() > trimmedName.length()) {
-      throw new AmbariException("Action name cannot contain white spaces");
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
index 56bd5e9..78131f2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
@@ -39,6 +39,8 @@ import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.util.StackExtensionHelper;
 import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.customactions.ActionDefinition;
+import org.apache.ambari.server.customactions.ActionDefinitionManager;
 import org.apache.ambari.server.orm.dao.MetainfoDAO;
 import org.apache.ambari.server.orm.entities.MetainfoEntity;
 import org.apache.ambari.server.state.ComponentInfo;
@@ -67,57 +69,52 @@ import com.google.inject.Singleton;
 @Singleton
 public class AmbariMetaInfo {
 
-  private final static Logger LOG = LoggerFactory.getLogger(AmbariMetaInfo.class);
-
   public static final String STACK_METAINFO_FILE_NAME = "metainfo.xml";
   public static final String SERVICES_FOLDER_NAME = "services";
   public static final String SERVICE_METAINFO_FILE_NAME = "metainfo.xml";
   public static final String SERVICE_CONFIG_FOLDER_NAME = "configuration";
   public static final String SERVICE_CONFIG_FILE_NAME_POSTFIX = ".xml";
   public static final String RCO_FILE_NAME = "role_command_order.json";
-  private static final String REPOSITORY_FILE_NAME = "repoinfo.xml";
-  private static final String REPOSITORY_FOLDER_NAME = "repos";
-  private static final String REPOSITORY_XML_PROPERTY_BASEURL = "baseurl";
-  // all the supported OS'es
-  private static final List<String> ALL_SUPPORTED_OS = Arrays.asList(
-      "centos5", "redhat5", "centos6", "redhat6", "oraclelinux5",
-      "oraclelinux6", "suse11", "sles11", "ubuntu12");
-  
   public static final String SERVICE_METRIC_FILE_NAME = "metrics.json";
-  private final static String HOOKS_DIR = "hooks";
-
   /**
    * This string is used in placeholder in places that are common for
    * all operating systems or in situations where os type is not important.
    */
   public static final String ANY_OS = "any";
-
   /**
    * Value for legacy xml files that don't contain schema property
    */
   public static final String SCHEMA_VERSION_LEGACY = "1.0";
-
   /**
    * Version of XML files with support of custom services and custom commands
    */
   public static final String SCHEMA_VERSION_2 = "2.0";
-
-
   public static final FilenameFilter FILENAME_FILTER = new FilenameFilter() {
     @Override
     public boolean accept(File dir, String s) {
       if (s.equals(".svn") || s.equals(".git") ||
-              s.equals(HOOKS_DIR)) // Hooks dir is not a service
+          s.equals(HOOKS_DIR)) // Hooks dir is not a service
+      {
         return false;
+      }
       return true;
     }
   };
-
+  private final static Logger LOG = LoggerFactory.getLogger(AmbariMetaInfo.class);
+  private static final String REPOSITORY_FILE_NAME = "repoinfo.xml";
+  private static final String REPOSITORY_FOLDER_NAME = "repos";
+  private static final String REPOSITORY_XML_PROPERTY_BASEURL = "baseurl";
+  // all the supported OS'es
+  private static final List<String> ALL_SUPPORTED_OS = Arrays.asList(
+      "centos5", "redhat5", "centos6", "redhat6", "oraclelinux5",
+      "oraclelinux6", "suse11", "sles11", "ubuntu12");
+  private final static String HOOKS_DIR = "hooks";
+  private final ActionDefinitionManager adManager = new ActionDefinitionManager();
   private String serverVersion = "undefined";
   private List<StackInfo> stacksResult = new ArrayList<StackInfo>();
   private File stackRoot;
   private File serverVersionFile;
-
+  private File customActionRoot;
   @Inject
   private MetainfoDAO metainfoDAO;
 
@@ -133,6 +130,7 @@ public class AmbariMetaInfo {
     String serverVersionFilePath = conf.getServerVersionFilePath();
     this.stackRoot = new File(stackPath);
     this.serverVersionFile = new File(serverVersionFilePath);
+    this.customActionRoot = new File(conf.getCustomActionDefinitionPath());
   }
 
   public AmbariMetaInfo(File stackRoot, File serverVersionFile) throws Exception {
@@ -150,6 +148,7 @@ public class AmbariMetaInfo {
     stacksResult = new ArrayList<StackInfo>();
     readServerVersion();
     getConfigurationInformation(stackRoot);
+    getCustomActionDefinitions(customActionRoot);
   }
 
   /**
@@ -464,11 +463,11 @@ public class AmbariMetaInfo {
   }
 
   public List<ServiceInfo> getSupportedServices(String stackName, String version) throws AmbariException {
-    List<ServiceInfo> servicesResulr = null;
+    List<ServiceInfo> servicesResult = null;
     StackInfo stack = getStackInfo(stackName, version);
     if (stack != null)
-      servicesResulr = stack.getServices();
-    return servicesResulr;
+      servicesResult = stack.getServices();
+    return servicesResult;
   }
 
   public List<StackInfo> getSupportedStacks() {
@@ -622,6 +621,41 @@ public class AmbariMetaInfo {
     serverVersion = new Scanner(versionFile).useDelimiter("\\Z").next();
   }
 
+  private void getCustomActionDefinitions(File customActionDefinitionRoot) throws JAXBException, AmbariException {
+    if (customActionDefinitionRoot != null) {
+      LOG.debug("Loading custom action definitions from "
+          + customActionDefinitionRoot.getAbsolutePath());
+
+      if (customActionDefinitionRoot.exists() && customActionDefinitionRoot.isDirectory()) {
+        adManager.readCustomActionDefinitions(customActionDefinitionRoot);
+      } else {
+        LOG.debug("No action definitions found at " + customActionDefinitionRoot.getAbsolutePath());
+      }
+    }
+  }
+
+  /**
+   * Get all action definitions
+   */
+  public List<ActionDefinition> getAllActionDefinition(){
+    return adManager.getAllActionDefinition();
+  }
+
+  /**
+   * Get action definitions based on the supplied name
+   */
+  public ActionDefinition getActionDefinition(String name){
+    return adManager.getActionDefinition(name);
+  }
+
+
+  /**
+   * Used for test purposes
+   */
+  public void addActionDefinition(ActionDefinition ad) throws AmbariException {
+    adManager.addActionDefinition(ad);
+  }
+
   private void getConfigurationInformation(File stackRoot) throws Exception {
     if (LOG.isDebugEnabled()) {
       LOG.debug("Loading stack information"

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index 7c8bbdf..b070d98 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -52,7 +52,7 @@ public class Configuration {
   public static final String BOOTSTRAP_DIR_DEFAULT = "/var/run/ambari-server/bootstrap";
   public static final String WEBAPP_DIR = "webapp.dir";
   public static final String BOOTSTRAP_SCRIPT = "bootstrap.script";
-  public static final String BOOTSTRAP_SCRIPT_DEFAULT =  "/usr/bin/ambari_bootstrap";
+  public static final String BOOTSTRAP_SCRIPT_DEFAULT = "/usr/bin/ambari_bootstrap";
   public static final String BOOTSTRAP_SETUP_AGENT_SCRIPT = "bootstrap.setup_agent.script";
   public static final String BOOTSTRAP_SETUP_AGENT_PASSWORD = "bootstrap.setup_agent.password";
   public static final String BOOTSTRAP_MASTER_HOSTNAME = "bootstrap.master_host_name";
@@ -81,7 +81,6 @@ public class Configuration {
   public static final String JAVA_HOME_KEY = "java.home";
   public static final String JDK_NAME_KEY = "jdk.name";
   public static final String JCE_NAME_KEY = "jce.name";
-
   public static final String CLIENT_SECURITY_KEY = "client.security";
   public static final String CLIENT_API_PORT_KEY = "client.api.port";
   public static final String CLIENT_API_SSL_PORT_KEY = "client.api.ssl.port";
@@ -96,7 +95,6 @@ public class Configuration {
   public static final String POSTGRES_DB_NAME = "postgres";
   public static final String ORACLE_DB_NAME = "oracle";
   public static final String MYSQL_DB_NAME = "mysql";
-
   public static final String OJDBC_JAR_NAME_KEY = "db.oracle.jdbc.name";
   public static final String OJDBC_JAR_NAME_DEFAULT = "ojdbc6.jar";
   public static final String MYSQL_JAR_NAME_KEY = "db.mysql.jdbc.name";
@@ -128,99 +126,119 @@ public class Configuration {
       "authorization.ldap.adminGroupMappingRules";
   public static final String LDAP_GROUP_SEARCH_FILTER_KEY =
       "authorization.ldap.groupSearchFilter";
-
   public static final String USER_ROLE_NAME_KEY =
       "authorization.userRoleName";
   public static final String ADMIN_ROLE_NAME_KEY =
       "authorization.adminRoleName";
-
   public static final String SERVER_EC_CACHE_SIZE = "server.ecCacheSize";
-  private static final long SERVER_EC_CACHE_SIZE_DEFAULT = 10000L;
-
   public static final String SERVER_PERSISTENCE_TYPE_KEY = "server.persistence.type";
   public static final String SERVER_JDBC_USER_NAME_KEY = "server.jdbc.user.name";
   public static final String SERVER_JDBC_USER_PASSWD_KEY = "server.jdbc.user.passwd";
   public static final String SERVER_JDBC_DRIVER_KEY = "server.jdbc.driver";
   public static final String SERVER_JDBC_URL_KEY = "server.jdbc.url";
   public static final String SERVER_JDBC_PROPERTIES_PREFIX = "server.jdbc.properties.";
-
-//  public static final String SERVER_RCA_PERSISTENCE_TYPE_KEY = "server.rca.persistence.type";
+  //  public static final String SERVER_RCA_PERSISTENCE_TYPE_KEY = "server.rca.persistence.type";
   public static final String SERVER_JDBC_RCA_USER_NAME_KEY = "server.jdbc.rca.user.name";
   public static final String SERVER_JDBC_RCA_USER_PASSWD_KEY = "server.jdbc.rca.user.passwd";
   public static final String SERVER_JDBC_RCA_DRIVER_KEY = "server.jdbc.rca.driver";
   public static final String SERVER_JDBC_RCA_URL_KEY = "server.jdbc.rca.url";
-
   public static final String SERVER_JDBC_GENERATE_TABLES_KEY = "server.jdbc.generateTables";
-
   public static final String JDBC_UNIT_NAME = "ambari-server";
-
   public static final String JDBC_LOCAL_URL = "jdbc:postgresql://localhost/";
   public static final String JDBC_LOCAL_DRIVER = "org.postgresql.Driver";
-
   public static final String JDBC_IN_MEMORY_URL = "jdbc:derby:memory:myDB/ambari;create=true";
   public static final String JDBC_IN_MEMROY_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
-
   public static final String HOSTNAME_MACRO = "{hostname}";
   public static final String JDBC_RCA_LOCAL_URL = "jdbc:postgresql://" + HOSTNAME_MACRO + "/ambarirca";
   public static final String JDBC_RCA_LOCAL_DRIVER = "org.postgresql.Driver";
-
-  private static final String SERVER_JDBC_USER_NAME_DEFAULT = "ambari-server";
-  private static final String SERVER_JDBC_USER_PASSWD_DEFAULT = "bigdata";
-
-  private static final String SERVER_JDBC_RCA_USER_NAME_DEFAULT = "mapred";
-  private static final String SERVER_JDBC_RCA_USER_PASSWD_DEFAULT = "mapred";
-
   public static final String OS_VERSION_KEY =
       "server.os_type";
-
   public static final String SRVR_HOSTS_MAPPING =
       "server.hosts.mapping";
-
   // Command parameter names
   public static final String UPGRADE_FROM_STACK = "source_stack_version";
   public static final String UPGRADE_TO_STACK = "target_stack_version";
-
   public static final String SSL_TRUSTSTORE_PATH_KEY = "ssl.trustStore.path";
   public static final String SSL_TRUSTSTORE_PASSWORD_KEY = "ssl.trustStore.password";
   public static final String SSL_TRUSTSTORE_TYPE_KEY = "ssl.trustStore.type";
   public static final String JAVAX_SSL_TRUSTSTORE = "javax.net.ssl.trustStore";
   public static final String JAVAX_SSL_TRUSTSTORE_PASSWORD = "javax.net.ssl.trustStorePassword";
   public static final String JAVAX_SSL_TRUSTSTORE_TYPE = "javax.net.ssl.trustStoreType";
-
   public static final String GANGLIA_HTTPS_KEY = "ganglia.https";
-  public static final String NAGIOS_HTTPS_KEY  = "nagios.https";
-
-  private static final String SRVR_TWO_WAY_SSL_DEFAULT = "false";
+  public static final String NAGIOS_HTTPS_KEY = "nagios.https";
   public static final String SRVR_TWO_WAY_SSL_PORT_DEFAULT = "8441";
   public static final String SRVR_ONE_WAY_SSL_PORT_DEFAULT = "8440";
-  private static final String SRVR_KSTR_DIR_DEFAULT = ".";
   public static final String SRVR_CRT_NAME_DEFAULT = "ca.crt";
   public static final String SRVR_KEY_NAME_DEFAULT = "ca.key";
   public static final String KSTR_NAME_DEFAULT = "keystore.p12";
-
   public static final String CLIENT_API_SSL_KSTR_NAME_DEFAULT = "https.keystore.p12";
   public static final String CLIENT_API_SSL_CRT_PASS_FILE_NAME_DEFAULT = "https.pass.txt";
   public static final String CLIENT_API_SSL_KEY_NAME_DEFAULT = "https.key";
   public static final String CLIENT_API_SSL_CRT_NAME_DEFAULT = "https.crt";
+  public static final String GLOBAL_CONFIG_TAG = "global";
+  public static final String RCA_ENABLED_PROPERTY = "rca_enabled";
+  public static final String HIVE_CONFIG_TAG = "hive-site";
+  public static final String HIVE_METASTORE_PASSWORD_PROPERTY =
+      "javax.jdo.option.ConnectionPassword";
+  public static final String MASTER_KEY_PERSISTED = "security.master" +
+      ".key.ispersisted";
+  public static final String MASTER_KEY_LOCATION = "security.master.key" +
+      ".location";
+  public static final String MASTER_KEY_ENV_PROP =
+      "AMBARI_SECURITY_MASTER_KEY";
+  public static final String MASTER_KEY_FILENAME_DEFAULT = "master";
+  /**
+   * Key for repo validation suffixes.
+   */
+  public static final String REPO_SUFFIX_KEY = "repo.validation.suffixes";
+  public static final String EXECUTION_SCHEDULER_CLUSTERED =
+      "server.execution.scheduler.isClustered";
+  public static final String EXECUTION_SCHEDULER_THREADS =
+      "server.execution.scheduler.maxThreads";
+  public static final String EXECUTION_SCHEDULER_CONNECTIONS =
+      "server.execution.scheduler.maxDbConnections";
+  public static final String EXECUTION_SCHEDULER_MISFIRE_TOLERATION =
+      "server.execution.scheduler.misfire.toleration.minutes";
+  public static final String EXECUTION_SCHEDULER_START_DELAY =
+      "server.execution.scheduler.start.delay.seconds";
+  public static final String DEFAULT_SCHEDULER_THREAD_COUNT = "5";
+  public static final String DEFAULT_SCHEDULER_MAX_CONNECTIONS = "5";
+  public static final String DEFAULT_EXECUTION_SCHEDULER_MISFIRE_TOLERATION = "480";
+  public static final String DEFAULT_SCHEDULER_START_DELAY_SECONDS = "120";
+  /**
+   * This key defines whether stages of parallel requests are executed in
+   * parallel or sequentally. Only stages from different requests
+   * running on not interfering host sets may be executed in parallel.
+   */
+  public static final String PARALLEL_STAGE_EXECUTION_KEY =
+      "server.stages.parallel";
+  public static final String AGENT_TASK_TIMEOUT_KEY = "agent.task.timeout";
+  public static final String AGENT_TASK_TIMEOUT_DEFAULT = "600";
 
-  private static final String API_CSRF_PREVENTION_DEFAULT = "true";
+  public static final String CUSTOM_ACTION_DEFINITION_KEY = "custom.action.definitions";
+  private static final String CUSTOM_ACTION_DEFINITION_DEF_VALUE =
+      "/var/lib/ambari-server/resources/custom_action_definitions";
 
-  private static final String SRVR_CRT_PASS_FILE_DEFAULT ="pass.txt";
+  private static final long SERVER_EC_CACHE_SIZE_DEFAULT = 10000L;
+  private static final String SERVER_JDBC_USER_NAME_DEFAULT = "ambari-server";
+  private static final String SERVER_JDBC_USER_PASSWD_DEFAULT = "bigdata";
+  private static final String SERVER_JDBC_RCA_USER_NAME_DEFAULT = "mapred";
+  private static final String SERVER_JDBC_RCA_USER_PASSWD_DEFAULT = "mapred";
+  private static final String SRVR_TWO_WAY_SSL_DEFAULT = "false";
+  private static final String SRVR_KSTR_DIR_DEFAULT = ".";
+  private static final String API_CSRF_PREVENTION_DEFAULT = "true";
+  private static final String SRVR_CRT_PASS_FILE_DEFAULT = "pass.txt";
   private static final String SRVR_CRT_PASS_LEN_DEFAULT = "50";
   private static final String PASSPHRASE_ENV_DEFAULT = "AMBARI_PASSPHRASE";
   private static final String RESOURCES_DIR_DEFAULT =
       "/var/share/ambari/resources/";
-  
-  private static final String  ANONYMOUS_AUDIT_NAME_KEY = "anonymous.audit.name";
-      
+  private static final String ANONYMOUS_AUDIT_NAME_KEY = "anonymous.audit.name";
   private static final String CLIENT_SECURITY_DEFAULT = "local";
   private static final int CLIENT_API_PORT_DEFAULT = 8080;
   private static final int CLIENT_API_SSL_PORT_DEFAULT = 8443;
-
   private static final String USER_ROLE_NAME_DEFAULT = "user";
   private static final String ADMIN_ROLE_NAME_DEFAULT = "admin";
   private static final String LDAP_BIND_ANONYMOUSLY_DEFAULT = "true";
-
   //TODO For embedded server only - should be removed later
   private static final String LDAP_PRIMARY_URL_DEFAULT = "localhost:33389";
   private static final String LDAP_BASE_DN_DEFAULT = "dc=ambari,dc=apache,dc=org";
@@ -233,75 +251,22 @@ public class Configuration {
   private static final String LDAP_ADMIN_GROUP_MAPPING_RULES_DEFAULT =
       "Ambari Administrators";
   private static final String LDAP_GROUP_SEARCH_FILTER_DEFAULT = "";
-
   //TODO for development purposes only, should be changed to 'false'
   private static final String SERVER_PERSISTENCE_TYPE_DEFAULT = "local";
-
   private static final String SERVER_CONNECTION_MAX_IDLE_TIME =
-    "server.connection.max.idle.millis";
-
-  public static final String GLOBAL_CONFIG_TAG = "global";
-  public static final String RCA_ENABLED_PROPERTY = "rca_enabled";
-  public static final String HIVE_CONFIG_TAG = "hive-site";
-  public static final String HIVE_METASTORE_PASSWORD_PROPERTY =
-    "javax.jdo.option.ConnectionPassword";
-
-  public static final String MASTER_KEY_PERSISTED = "security.master" +
-    ".key.ispersisted";
-  public static final String MASTER_KEY_LOCATION = "security.master.key" +
-    ".location";
-  public static final String MASTER_KEY_ENV_PROP =
-    "AMBARI_SECURITY_MASTER_KEY";
-  public static final String MASTER_KEY_FILENAME_DEFAULT = "master";
-  
-  /**
-   * Key for repo validation suffixes.
-   */
-  public static final String REPO_SUFFIX_KEY = "repo.validation.suffixes";
+      "server.connection.max.idle.millis";
   /**
    * Default for repo validation suffixes.
    */
   private static final String REPO_SUFFIX_DEFAULT = "/repodata/repomd.xml";
-
-  public static final String EXECUTION_SCHEDULER_CLUSTERED =
-    "server.execution.scheduler.isClustered";
-  public static final String EXECUTION_SCHEDULER_THREADS =
-    "server.execution.scheduler.maxThreads";
-  public static final String EXECUTION_SCHEDULER_CONNECTIONS =
-    "server.execution.scheduler.maxDbConnections";
-  public static final String EXECUTION_SCHEDULER_MISFIRE_TOLERATION =
-    "server.execution.scheduler.misfire.toleration.minutes";
-  public static final String EXECUTION_SCHEDULER_START_DELAY =
-    "server.execution.scheduler.start.delay.seconds";
-
-  public static final String DEFAULT_SCHEDULER_THREAD_COUNT = "5";
-  public static final String DEFAULT_SCHEDULER_MAX_CONNECTIONS = "5";
-  public static final String DEFAULT_EXECUTION_SCHEDULER_MISFIRE_TOLERATION = "480";
-  public static final String DEFAULT_SCHEDULER_START_DELAY_SECONDS = "120";
-
-
-  /**
-   * This key defines whether stages of parallel requests are executed in
-   * parallel or sequentally. Only stages from different requests
-   * running on not interfering host sets may be executed in parallel.
-   */
-  public static final String PARALLEL_STAGE_EXECUTION_KEY =
-          "server.stages.parallel";
   private static final String PARALLEL_STAGE_EXECUTION_DEFAULT = "true";
-
-  public static final String AGENT_TASK_TIMEOUT_KEY = "agent.task.timeout";
-  public static final String AGENT_TASK_TIMEOUT_DEFAULT = "600";
-
   private static final Logger LOG = LoggerFactory.getLogger(
       Configuration.class);
-
   private Properties properties;
-
   private Map<String, String> configsMap;
-
   private CredentialProvider credentialProvider = null;
   private volatile boolean credentialProviderInitialized = false;
-  private Map<String,String> customDbProperties = null;
+  private Map<String, String> customDbProperties = null;
 
   public Configuration() {
     this(readConfigFile());
@@ -929,18 +894,26 @@ public class Configuration {
     return "true".equalsIgnoreCase(configsMap.get(PARALLEL_STAGE_EXECUTION_KEY));
   }
 
+  public String getCustomActionDefinitionPath() {
+    String value = configsMap.get(CUSTOM_ACTION_DEFINITION_KEY);
+    if (value == null) {
+      value = CUSTOM_ACTION_DEFINITION_DEF_VALUE;
+    }
+    return value;
+  }
+
   /**
    * @return default task timeout in seconds (string representation). This value
-   * is used at python (agent) code.
+   *         is used at python (agent) code.
    */
   public String getDefaultAgentTaskTimeout() {
-    String value =  properties.getProperty(AGENT_TASK_TIMEOUT_KEY, AGENT_TASK_TIMEOUT_DEFAULT);
+    String value = properties.getProperty(AGENT_TASK_TIMEOUT_KEY, AGENT_TASK_TIMEOUT_DEFAULT);
     if (StringUtils.isNumeric(value)) {
       return value;
     } else {
       LOG.warn(String.format("Value of %s (%s) should be a number, " +
-              "falling back to default value (%s)",
-              AGENT_TASK_TIMEOUT_KEY, value, AGENT_TASK_TIMEOUT_DEFAULT));
+          "falling back to default value (%s)",
+          AGENT_TASK_TIMEOUT_KEY, value, AGENT_TASK_TIMEOUT_DEFAULT));
       return AGENT_TASK_TIMEOUT_DEFAULT;
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionResponse.java
index f85f640..e7edafb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionResponse.java
@@ -18,8 +18,6 @@
 
 package org.apache.ambari.server.controller;
 
-import org.apache.ambari.server.actionmanager.ActionDefinition;
-
 /**
  * Used to respond to GET requests for actions
  */
@@ -47,15 +45,6 @@ public class ActionResponse {
     setDefaultTimeout(defaultTimeout);
   }
 
-  public ActionResponse(ActionDefinition ad) {
-    this(ad.getActionName(), ad.getActionType().toString(), ad.getInputs(),
-        null == ad.getTargetService() ? "" : ad.getTargetService().toString(),
-        null == ad.getTargetComponent() ? "" : ad.getTargetComponent().toString(),
-        ad.getDescription(),
-        null == ad.getTargetType() ? "" : ad.getTargetType().toString(),
-        ad.getDefaultTimeout().toString());
-  }
-
   public String getActionName() {
     return actionName;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariActionExecutionHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariActionExecutionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariActionExecutionHelper.java
index d985d9f..70f9e58 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariActionExecutionHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariActionExecutionHelper.java
@@ -22,13 +22,13 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.Role;
 import org.apache.ambari.server.RoleCommand;
 import org.apache.ambari.server.StackAccessException;
-import org.apache.ambari.server.actionmanager.ActionDefinition;
 import org.apache.ambari.server.actionmanager.ActionManager;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.TargetHostType;
 import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.customactions.ActionDefinition;
 import org.apache.ambari.server.metadata.ActionMetadata;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -41,9 +41,15 @@ import org.apache.ambari.server.utils.StageUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
 
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.*;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.COMMAND_TIMEOUT;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCHEMA_VERSION;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT_TYPE;
 
 /**
  * Helper class containing logic to process custom action execution requests
@@ -51,14 +57,13 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.*;
 public class AmbariActionExecutionHelper {
   private final static Logger LOG =
       LoggerFactory.getLogger(AmbariActionExecutionHelper.class);
+  private static final String TYPE_PYTHON = "PYTHON";
   private ActionMetadata actionMetadata;
   private Clusters clusters;
   private AmbariManagementControllerImpl amcImpl;
   private ActionManager actionManager;
   private AmbariMetaInfo ambariMetaInfo;
 
-  private static final String TYPE_PYTHON = "PYTHON";
-
   public AmbariActionExecutionHelper(ActionMetadata actionMetadata, Clusters clusters,
                                      AmbariManagementControllerImpl amcImpl) {
     this.amcImpl = amcImpl;
@@ -82,7 +87,7 @@ public class AmbariActionExecutionHelper {
       throw new AmbariException("Action name must be specified");
     }
 
-    ActionDefinition actionDef = actionManager.getActionDefinition(actionRequest.getActionName());
+    ActionDefinition actionDef = ambariMetaInfo.getActionDefinition(actionRequest.getActionName());
     if (actionDef == null) {
       throw new AmbariException("Action " + actionRequest.getActionName() + " does not exist");
     }
@@ -164,7 +169,7 @@ public class AmbariActionExecutionHelper {
       }
     }
 
-    if (actionDef.getTargetType() == TargetHostType.SPECIFIC
+    if (TargetHostType.SPECIFIC.name().equals(actionDef.getTargetType())
         || (targetService.isEmpty() && targetService.isEmpty())) {
       if (actionRequest.getHosts().size() == 0) {
         throw new AmbariException("Action " + actionRequest.getActionName() + " requires explicit target host(s)" +

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index 7aa39a8..2b93e54 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -18,12 +18,19 @@
 
 package org.apache.ambari.server.controller;
 
-import java.security.SecureRandom;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-
-import org.apache.ambari.server.actionmanager.*;
+import com.google.gson.Gson;
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
+import com.google.inject.persist.jpa.JpaPersistModule;
+import org.apache.ambari.server.actionmanager.ActionDBAccessor;
+import org.apache.ambari.server.actionmanager.ActionDBAccessorImpl;
+import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
+import org.apache.ambari.server.actionmanager.RequestFactory;
+import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.ComponentResourceProvider;
 import org.apache.ambari.server.controller.internal.HostComponentResourceProvider;
@@ -64,14 +71,24 @@ import org.apache.ambari.server.state.svccomphost.ServiceComponentHostImpl;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.crypto.password.StandardPasswordEncoder;
 
-import com.google.gson.Gson;
-import com.google.inject.AbstractModule;
-import com.google.inject.Scopes;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
-import com.google.inject.name.Names;
-import com.google.inject.persist.jpa.JpaPersistModule;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
 
-import static org.eclipse.persistence.config.PersistenceUnitProperties.*;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.CREATE_JDBC_DDL_FILE;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.CREATE_ONLY;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.CREATE_OR_EXTEND;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.DDL_BOTH_GENERATION;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.DDL_GENERATION;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.DDL_GENERATION_MODE;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.DROP_AND_CREATE;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.DROP_JDBC_DDL_FILE;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.JDBC_DRIVER;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.JDBC_PASSWORD;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.JDBC_URL;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.JDBC_USER;
+import static org.eclipse.persistence.config.PersistenceUnitProperties.THROW_EXCEPTIONS;
 
 /**
  * Used for injection purposes.
@@ -108,7 +125,6 @@ public class ControllerModule extends AbstractModule {
     bind(Clusters.class).to(ClustersImpl.class);
     bind(AmbariCustomCommandExecutionHelper.class);
     bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class);
-    bind(CustomActionDBAccessor.class).to(CustomActionDBAccessorImpl.class);
     bindConstant().annotatedWith(Names.named("schedulerSleeptime")).to(10000L);
 
     // This time is added to summary timeout time of all tasks in stage
@@ -141,13 +157,13 @@ public class ControllerModule extends AbstractModule {
 
     // custom jdbc properties
     Map<String, String> custom = configuration.getDatabaseCustomProperties();
-    
+
     if (0 != custom.size()) {
       for (Entry<String, String> entry : custom.entrySet()) {
         properties.setProperty("eclipselink.jdbc.property." + entry.getKey(),
-           entry.getValue());
+            entry.getValue());
       }
-    }    
+    }
 
     switch (persistenceType) {
       case IN_MEMORY:
@@ -202,16 +218,16 @@ public class ControllerModule extends AbstractModule {
         Host.class, HostImpl.class).build(HostFactory.class));
     install(new FactoryModuleBuilder().implement(
         Service.class, ServiceImpl.class).build(ServiceFactory.class));
-   
-    
+
+
     install(new FactoryModuleBuilder()
         .implement(ResourceProvider.class, Names.named("host"), HostResourceProvider.class)
         .implement(ResourceProvider.class, Names.named("hostComponent"), HostComponentResourceProvider.class)
         .implement(ResourceProvider.class, Names.named("service"), ServiceResourceProvider.class)
         .implement(ResourceProvider.class, Names.named("component"), ComponentResourceProvider.class)
-        .build(ResourceProviderFactory.class)); 
+        .build(ResourceProviderFactory.class));
+
 
-    
     install(new FactoryModuleBuilder().implement(
         ServiceComponent.class, ServiceComponentImpl.class).build(
         ServiceComponentFactory.class));
@@ -221,9 +237,9 @@ public class ControllerModule extends AbstractModule {
     install(new FactoryModuleBuilder().implement(
         Config.class, ConfigImpl.class).build(ConfigFactory.class));
     install(new FactoryModuleBuilder().implement(
-      ConfigGroup.class, ConfigGroupImpl.class).build(ConfigGroupFactory.class));
+        ConfigGroup.class, ConfigGroupImpl.class).build(ConfigGroupFactory.class));
     install(new FactoryModuleBuilder().implement(RequestExecution.class,
-      RequestExecutionImpl.class).build(RequestExecutionFactory.class));
+        RequestExecutionImpl.class).build(RequestExecutionFactory.class));
     install(new FactoryModuleBuilder().build(StageFactory.class));
     install(new FactoryModuleBuilder().build(RequestFactory.class));
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ActionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ActionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ActionResourceProvider.java
index 54ab41c..faef86c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ActionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ActionResourceProvider.java
@@ -20,15 +20,11 @@
 package org.apache.ambari.server.controller.internal;
 
 import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.actionmanager.ActionDefinition;
 import org.apache.ambari.server.actionmanager.ActionManager;
-import org.apache.ambari.server.actionmanager.ActionType;
-import org.apache.ambari.server.actionmanager.CustomActionDBAccessorImpl;
-import org.apache.ambari.server.actionmanager.TargetHostType;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.ActionRequest;
 import org.apache.ambari.server.controller.ActionResponse;
 import org.apache.ambari.server.controller.AmbariManagementController;
-import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -40,6 +36,7 @@ import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.customactions.ActionDefinition;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -67,7 +64,6 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
       .getPropertyId("Actions", "default_timeout");
   private static Set<String> pkPropertyIds = new HashSet<String>(
       Arrays.asList(new String[]{ACTION_NAME_PROPERTY_ID}));
-  private Boolean enableExperimental = false;
 
   public ActionResourceProvider(Set<String> propertyIds,
                                 Map<Type, String> keyPropertyIds,
@@ -75,14 +71,6 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
     super(propertyIds, keyPropertyIds, managementController);
   }
 
-  public Boolean getEnableExperimental() {
-    return enableExperimental;
-  }
-
-  public void setEnableExperimental(Boolean enabled) {
-    enableExperimental = enabled;
-  }
-
   @Override
   public RequestStatus createResources(Request request)
       throws SystemException,
@@ -90,29 +78,8 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
       ResourceAlreadyExistsException,
       NoSuchParentResourceException {
 
-    if (!getEnableExperimental()) {
-      throw new UnsupportedOperationException("Not currently supported.");
-    }
-
-    for (final Map<String, Object> properties : request.getProperties()) {
-      createResources(new Command<Void>() {
-        @Override
-        public Void invoke() throws AmbariException {
-          ActionRequest actionReq = getRequest(properties);
-          LOG.info("Received a create request for Action with"
-              + ", actionName = " + actionReq.getActionName()
-              + ", actionType = " + actionReq.getActionType()
-              + ", description = " + actionReq.getDescription()
-              + ", service = " + actionReq.getTargetService());
+    throw new UnsupportedOperationException("Not currently supported.");
 
-          createActionDefinition(actionReq);
-          return null;
-        }
-      });
-    }
-    notifyCreate(Type.Action, request);
-
-    return getRequestStatus(null);
   }
 
   @Override
@@ -120,34 +87,8 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
       throws SystemException, UnsupportedPropertyException,
       NoSuchResourceException, NoSuchParentResourceException {
 
-    if (!getEnableExperimental()) {
-      throw new UnsupportedOperationException("Not currently supported.");
-    }
+    throw new UnsupportedOperationException("Not currently supported.");
 
-    final Set<ActionRequest> requests = new HashSet<ActionRequest>();
-    RequestStatusResponse response;
-
-    for (Map<String, Object> requestPropertyMap : request.getProperties()) {
-      Set<Map<String, Object>> propertyMaps = getPropertyMaps(requestPropertyMap, predicate);
-      for (Map<String, Object> propertyMap : propertyMaps) {
-        ActionRequest actionReq = getRequest(propertyMap);
-        LOG.info("Received a update request for Action with"
-            + ", actionName = " + actionReq.getActionName()
-            + ", actionType = " + actionReq.getActionType()
-            + ", description = " + actionReq.getDescription()
-            + ", timeout = " + actionReq.getDefaultTimeout());
-
-        requests.add(actionReq);
-      }
-    }
-    response = modifyResources(new Command<RequestStatusResponse>() {
-      @Override
-      public RequestStatusResponse invoke() throws AmbariException {
-        return updateActionDefinitions(requests, request.getRequestInfoProperties());
-      }
-    });
-    notifyUpdate(Type.Action, request, predicate);
-    return getRequestStatus(response);
   }
 
   @Override
@@ -205,23 +146,7 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
   public RequestStatus deleteResources(Predicate predicate)
       throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
 
-    if (!getEnableExperimental()) {
-      throw new UnsupportedOperationException("Not currently supported.");
-    }
-
-    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
-      final ActionRequest request = getRequest(propertyMap);
-      try {
-        LOG.info("Received a delete request for Action with"
-            + ", actionName = " + request.getActionName());
-
-        deleteActionDefinition(request);
-      } catch (AmbariException ex) {
-        throw new NoSuchResourceException(ex.getMessage());
-      }
-    }
-    notifyDelete(Type.Action, predicate);
-    return getRequestStatus(null);
+    throw new UnsupportedOperationException("Not currently supported.");
   }
 
   private ActionRequest getRequest(Map<String, Object> properties) {
@@ -247,28 +172,8 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
     return getManagementController().getActionManager();
   }
 
-  protected synchronized void createActionDefinition(ActionRequest request)
-      throws AmbariException {
-    if (request.getActionName() == null
-        || request.getActionName().isEmpty()) {
-      throw new IllegalArgumentException("Action name should be provided");
-    }
-
-    LOG.info("Received a createActionDefinition request = " + request.toString());
-    if (request.getTargetType() == null || request.getActionType() == null) {
-      throw new AmbariException("Both target_type and action_type must be specified.");
-    }
-    TargetHostType targetType = TargetHostType.valueOf(request.getTargetType());
-    ActionType actionType = ActionType.valueOf(request.getActionType());
-
-    Short defaultTimeout = CustomActionDBAccessorImpl.MIN_TIMEOUT;
-    if (request.getDefaultTimeout() != null && !request.getDefaultTimeout().isEmpty()) {
-      defaultTimeout = Short.parseShort(request.getDefaultTimeout());
-    }
-
-    getActionManager().createActionDefinition(request.getActionName(), actionType, request.getInputs(),
-        request.getDescription(), request.getTargetService(), request.getTargetComponent(),
-        targetType, defaultTimeout);
+  private AmbariMetaInfo getAmbariMetaInfo() {
+    return getManagementController().getAmbariMetaInfo();
   }
 
   protected synchronized Set<ActionResponse> getActionDefinitions(Set<ActionRequest> requests)
@@ -276,45 +181,18 @@ public class ActionResourceProvider extends AbstractControllerResourceProvider {
     Set<ActionResponse> responses = new HashSet<ActionResponse>();
     for (ActionRequest request : requests) {
       if (request.getActionName() == null) {
-        List<ActionDefinition> ads = getActionManager().getAllActionDefinition();
+        List<ActionDefinition> ads = getAmbariMetaInfo().getAllActionDefinition();
         for (ActionDefinition ad : ads) {
-          responses.add(new ActionResponse(ad));
+          responses.add(ad.convertToResponse());
         }
       } else {
-        ActionDefinition ad = getActionManager().getActionDefinition(request.getActionName());
+        ActionDefinition ad = getAmbariMetaInfo().getActionDefinition(request.getActionName());
         if (ad != null) {
-          responses.add(new ActionResponse(ad));
+          responses.add(ad.convertToResponse());
         }
       }
     }
 
     return responses;
   }
-
-  protected synchronized RequestStatusResponse updateActionDefinitions(Set<ActionRequest> requests,
-                                                                       Map<String, String> requestProperties)
-      throws AmbariException {
-    RequestStatusResponse response = null;
-    for (ActionRequest request : requests) {
-      if (null != request.getInputs() || null != request.getTargetService()
-          || null != request.getTargetComponent()) {
-        throw new AmbariException("Cannot update inputs, target_service, or target_component");
-      }
-      TargetHostType targetType = request.getTargetType() == null ? null
-          : TargetHostType.valueOf(request.getTargetType());
-      ActionType actionType = request.getActionType() == null ? null : ActionType.valueOf(request.getActionType());
-      Short defaultTimeout = null;
-      if (request.getDefaultTimeout() != null && !request.getDefaultTimeout().isEmpty()) {
-        defaultTimeout = Short.parseShort(request.getDefaultTimeout());
-      }
-      getActionManager().updateActionDefinition(request.getActionName(), actionType,
-          request.getDescription(), targetType, defaultTimeout);
-    }
-    return response;
-  }
-
-  protected synchronized void deleteActionDefinition(ActionRequest request)
-      throws AmbariException {
-    getActionManager().deleteActionDefinition(request.getActionName());
-  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinition.java
new file mode 100644
index 0000000..1f189a3
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinition.java
@@ -0,0 +1,132 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.server.customactions;
+
+import org.apache.ambari.server.actionmanager.ActionType;
+import org.apache.ambari.server.actionmanager.TargetHostType;
+import org.apache.ambari.server.controller.ActionResponse;
+
+/**
+ * The resource describing the definition of an action
+ */
+public class ActionDefinition {
+  private String actionName;
+  private ActionType actionType;
+  private String inputs;
+  private String targetService;
+  private String targetComponent;
+  private String description;
+  private TargetHostType targetType;
+  private Short defaultTimeout;
+
+  /**
+   * Create an instance of ActionDefinition
+   *
+   * @param actionName      The name of the action
+   * @param actionType      The type fo the action
+   * @param inputs          Expected input of the action
+   * @param targetService   Target service type (e.g. HDFS)
+   * @param targetComponent Target component type (e.g. DATANODE)
+   * @param description     Short description of the action
+   * @param targetType      Selection criteria for target hosts
+   * @param defaultTimeout  The timeout value for this action when executed
+   */
+  public ActionDefinition(String actionName, ActionType actionType, String inputs,
+                          String targetService, String targetComponent, String description,
+                          TargetHostType targetType, Short defaultTimeout) {
+    setActionName(actionName);
+    setActionType(actionType);
+    setInputs(inputs);
+    setTargetService(targetService);
+    setTargetComponent(targetComponent);
+    setDescription(description);
+    setTargetType(targetType);
+    setDefaultTimeout(defaultTimeout);
+  }
+
+  public String getActionName() {
+    return actionName;
+  }
+
+  public void setActionName(String actionName) {
+    this.actionName = actionName;
+  }
+
+  public ActionType getActionType() {
+    return actionType;
+  }
+
+  public void setActionType(ActionType actionType) {
+    this.actionType = actionType;
+  }
+
+  public String getInputs() {
+    return inputs;
+  }
+
+  public void setInputs(String inputs) {
+    this.inputs = inputs;
+  }
+
+  public String getTargetService() {
+    return targetService;
+  }
+
+  public void setTargetService(String targetService) {
+    this.targetService = targetService;
+  }
+
+  public String getTargetComponent() {
+    return targetComponent;
+  }
+
+  public void setTargetComponent(String targetComponent) {
+    this.targetComponent = targetComponent;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public TargetHostType getTargetType() {
+    return targetType;
+  }
+
+  public void setTargetType(TargetHostType targetType) {
+    this.targetType = targetType;
+  }
+
+  public Short getDefaultTimeout() {
+    return defaultTimeout;
+  }
+
+  public void setDefaultTimeout(Short defaultTimeout) {
+    this.defaultTimeout = defaultTimeout;
+  }
+
+  public ActionResponse convertToResponse() {
+    return new ActionResponse(getActionName(), getActionType().name(), getInputs(),
+        getTargetService(), getTargetComponent(), getDescription(), getTargetType().name(),
+        getDefaultTimeout().toString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionManager.java b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionManager.java
new file mode 100644
index 0000000..e058ce7
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionManager.java
@@ -0,0 +1,202 @@
+/**
+ * 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.
+ */
+package org.apache.ambari.server.customactions;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.actionmanager.ActionType;
+import org.apache.ambari.server.actionmanager.TargetHostType;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.UnmarshalException;
+import javax.xml.bind.Unmarshaller;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manages Action definitions read from XML files
+ */
+public class ActionDefinitionManager {
+  public static final Short MIN_TIMEOUT = 60;
+  private final static Logger LOG = LoggerFactory
+      .getLogger(ActionDefinitionManager.class);
+  private static final Map<Class<?>, JAXBContext> _jaxbContexts =
+      new HashMap<Class<?>, JAXBContext>();
+  private static final Short MAX_TIMEOUT = 600;
+
+  static {
+    try {
+      JAXBContext ctx = JAXBContext.newInstance(ActionDefinitionXml.class);
+      _jaxbContexts.put(ActionDefinitionXml.class, ctx);
+    } catch (JAXBException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private final Map<String, ActionDefinition> actionDefinitionMap = new HashMap<String, ActionDefinition>();
+
+  public ActionDefinitionManager() {
+  }
+
+  public static <T> T unmarshal(Class<T> clz, File file) throws JAXBException {
+    Unmarshaller u = _jaxbContexts.get(clz).createUnmarshaller();
+
+    return clz.cast(u.unmarshal(file));
+  }
+
+  private <E extends Enum<E>> E safeValueOf(Class<E> enumm, String s, StringBuilder reason) {
+    if (s == null || s.length() == 0) {
+      return null;
+    }
+
+    try {
+      return Enum.valueOf(enumm, s);
+    } catch (IllegalArgumentException iaex) {
+      reason.append("Invalid value provided for " + enumm.getName());
+      return null;
+    }
+  }
+
+  public void readCustomActionDefinitions(File customActionDefinitionRoot) throws JAXBException, AmbariException {
+    if (customActionDefinitionRoot == null
+        || !customActionDefinitionRoot.exists()
+        || !customActionDefinitionRoot.canRead()) {
+      LOG.warn("Cannot read custom action definitions. " +
+          customActionDefinitionRoot == null ? "" : "Check path " + customActionDefinitionRoot.getAbsolutePath());
+    }
+
+    File[] customActionDefinitionFiles
+        = customActionDefinitionRoot.listFiles(AmbariMetaInfo.FILENAME_FILTER);
+
+    if (customActionDefinitionFiles != null) {
+      for (File definitionFile : customActionDefinitionFiles) {
+        ActionDefinitionXml adx = null;
+        try {
+          adx = unmarshal(ActionDefinitionXml.class, definitionFile);
+        } catch (UnmarshalException uex) {
+          LOG.warn("Encountered badly formed action definition file - " + definitionFile.getAbsolutePath());
+          continue;
+        }
+        for (ActionDefinitionSpec ad : adx.actionDefinitions()) {
+          LOG.debug("Read action definition = " + ad.toString());
+          StringBuilder errorReason =
+              new StringBuilder("Error while parsing action definition. ").append(ad.toString()).append(" --- ");
+
+          TargetHostType targetType = safeValueOf(TargetHostType.class, ad.getTargetType(), errorReason);
+          ActionType actionType = safeValueOf(ActionType.class, ad.getActionType(), errorReason);
+
+          Short defaultTimeout = MIN_TIMEOUT;
+          if (ad.getDefaultTimeout() != null && !ad.getDefaultTimeout().isEmpty()) {
+            defaultTimeout = Short.parseShort(ad.getDefaultTimeout());
+          }
+
+          if (isValidActionDefinition(ad, actionType, defaultTimeout, errorReason)) {
+            String actionName = ad.getActionName();
+            if (actionDefinitionMap.containsKey(actionName)) {
+              LOG.warn("Ignoring action definition as a different definition by that name already exists. "
+                  + ad.toString());
+              continue;
+            }
+
+            actionDefinitionMap.put(ad.getActionName(), new ActionDefinition(ad.getActionName(), actionType,
+                ad.getInputs(), ad.getTargetService(), ad.getTargetComponent(), ad.getDescription(), targetType, defaultTimeout));
+            LOG.info("Added custom action definition for " + ad.getActionName());
+          } else {
+            LOG.warn(errorReason.toString());
+          }
+        }
+      }
+    }
+  }
+
+  private boolean isValidActionDefinition(ActionDefinitionSpec ad, ActionType actionType,
+                                          Short defaultTimeout, StringBuilder reason) {
+    if (isValidActionName(ad.getActionName(), reason)) {
+
+      if (defaultTimeout < MIN_TIMEOUT || defaultTimeout > MAX_TIMEOUT) {
+        reason.append("Default timeout should be between " + MIN_TIMEOUT + " and " + MAX_TIMEOUT);
+        return false;
+      }
+
+      if (actionType == null || actionType == ActionType.SYSTEM_DISABLED) {
+        reason.append("Action type cannot be " + actionType);
+        return false;
+      }
+
+      if (ad.getDescription() == null || ad.getDescription().isEmpty()) {
+        reason.append("Action description cannot be empty");
+        return false;
+      }
+
+      if (ad.getTargetService() == null || ad.getTargetService().isEmpty()) {
+        if (ad.getTargetComponent() != null && !ad.getTargetComponent().isEmpty()) {
+          reason.append("Target component cannot be specified unless target service is specified");
+          return false;
+        }
+      }
+
+      if (ad.getInputs() != null && !ad.getInputs().isEmpty()) {
+        String[] parameters = ad.getInputs().split(",");
+        for (String parameter : parameters) {
+          if (parameter.trim().isEmpty()) {
+            reason.append("Empty parameter cannot be specified as an input parameter");
+          }
+        }
+      }
+    } else {
+      return false;
+    }
+
+    return true;
+  }
+
+  public List<ActionDefinition> getAllActionDefinition() {
+    return new ArrayList<ActionDefinition>(actionDefinitionMap.values());
+  }
+
+  public ActionDefinition getActionDefinition(String name) {
+    return actionDefinitionMap.get(name);
+  }
+
+  public void addActionDefinition(ActionDefinition ad) throws AmbariException {
+    if (!actionDefinitionMap.containsKey(ad.getActionName())) {
+      actionDefinitionMap.put(ad.getActionName(), ad);
+    } else {
+      throw new AmbariException("Action definition by name " + ad.getActionName() + " already exists.");
+    }
+  }
+
+  private boolean isValidActionName(String actionName, StringBuilder reason) {
+    if (actionName == null || actionName.isEmpty()) {
+      reason.append("Action name cannot be empty");
+      return false;
+    }
+    String trimmedName = actionName.replaceAll("\\s+", "");
+    if (actionName.length() > trimmedName.length()) {
+      reason.append("Action name cannot contain white spaces");
+      return false;
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8e8a9a47/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionSpec.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionSpec.java b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionSpec.java
new file mode 100644
index 0000000..2dfa1f2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/customactions/ActionDefinitionSpec.java
@@ -0,0 +1,139 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.server.customactions;
+
+public class ActionDefinitionSpec {
+  private String actionName;
+  private String actionType;
+  private String inputs;
+  private String targetService;
+  private String targetComponent;
+  private String description;
+  private String targetType;
+  private String defaultTimeout;
+
+  public String getTargetComponent() {
+    return targetComponent;
+  }
+
+  public void setTargetComponent(String targetComponent) {
+    this.targetComponent = targetComponent;
+  }
+
+  public String getTargetType() {
+    return targetType;
+  }
+
+  public void setTargetType(String targetType) {
+    this.targetType = targetType;
+  }
+
+  public String getDefaultTimeout() {
+    return defaultTimeout;
+  }
+
+  public void setDefaultTimeout(String defaultTimeout) {
+    this.defaultTimeout = defaultTimeout;
+  }
+
+  public String getActionName() {
+    return actionName;
+  }
+
+  public void setActionName(String actionName) {
+    this.actionName = actionName;
+  }
+
+  public String getActionType() {
+    return actionType;
+  }
+
+  public void setActionType(String actionType) {
+    this.actionType = actionType;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public String getInputs() {
+    return inputs;
+  }
+
+  public void setInputs(String inputs) {
+    this.inputs = inputs;
+  }
+
+  public String getTargetService() {
+    return targetService;
+  }
+
+  public void setTargetService(String targetService) {
+    this.targetService = targetService;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result
+        + ((actionName == null) ? 0 : actionName.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    ActionDefinitionSpec other = (ActionDefinitionSpec) obj;
+    if (description == null) {
+      if (other.description != null) {
+        return false;
+      }
+    } else if (!description.equals(other.description)) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder().append("ActionDefinition:")
+        .append(" actionName: ").append(actionName)
+        .append(" actionType: ").append(actionType)
+        .append(" inputs: ").append(inputs)
+        .append(" description: ").append(description)
+        .append(" targetService: ").append(targetService)
+        .append(" targetComponent: ").append(targetComponent)
+        .append(" defaultTimeout: ").append(defaultTimeout)
+        .append(" targetType: ").append(targetType)
+        .toString();
+  }
+}