ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From smoha...@apache.org
Subject [3/3] git commit: AMBARI 3608. Custom Action: Add support for Custom Action Definition
Date Mon, 04 Nov 2013 18:05:16 GMT
AMBARI 3608. Custom Action: Add support for Custom Action Definition


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

Branch: refs/heads/trunk
Commit: 8b0e64c5121ee14acdafa624681c9f291a390bb1
Parents: 9e3d193
Author: Sumit Mohanty <smohanty@hortonworks.com>
Authored: Mon Nov 4 09:46:44 2013 -0800
Committer: Sumit Mohanty <smohanty@hortonworks.com>
Committed: Mon Nov 4 09:46:44 2013 -0800

----------------------------------------------------------------------
 .../server/actionmanager/ActionDefinition.java  | 138 ++++++++
 .../server/actionmanager/ActionManager.java     |  50 ++-
 .../ambari/server/actionmanager/ActionType.java |  30 ++
 .../actionmanager/CustomActionDBAccessor.java   |  82 +++++
 .../CustomActionDBAccessorImpl.java             | 222 +++++++++++++
 .../server/actionmanager/TargetHostType.java    |  28 ++
 .../server/api/services/ActionService.java      | 140 ++++----
 .../server/api/services/RequestService.java     |  17 +
 .../server/api/services/ServiceService.java     |  11 -
 .../ambari/server/controller/ActionRequest.java | 120 +++++--
 .../server/controller/ActionResponse.java       | 165 +++++++++-
 .../controller/AmbariManagementController.java  |  16 +-
 .../AmbariManagementControllerImpl.java         | 119 ++++---
 .../server/controller/ControllerModule.java     |   3 +
 .../server/controller/ExecuteActionRequest.java |  89 +++++
 .../internal/ActionResourceProvider.java        | 320 ++++++++++++++----
 .../server/controller/internal/RequestImpl.java |   4 +-
 .../internal/RequestResourceProvider.java       | 145 ++++++---
 .../ambari/server/controller/spi/Resource.java  |   4 +-
 .../server/orm/dao/ActionDefinitionDAO.java     |  71 ++++
 .../server/orm/dao/ExecutionCommandDAO.java     |  12 +-
 .../server/orm/entities/ActionEntity.java       | 154 +++++++++
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   3 +
 .../src/main/resources/META-INF/persistence.xml |   2 +
 .../src/main/resources/key_properties.json      | 208 ++++++------
 .../src/main/resources/properties.json          |   9 +-
 .../actionmanager/TestActionDBAccessorImpl.java |   2 +-
 .../server/actionmanager/TestActionManager.java |   6 +-
 .../actionmanager/TestActionScheduler.java      |   6 +-
 .../TestCustomActionDBAccessorImpl.java         | 124 +++++++
 .../server/agent/TestHeartbeatHandler.java      |   4 +-
 .../server/api/services/ActionServiceTest.java  |  74 +++--
 .../server/controller/ActionRequestTest.java    |  53 +++
 .../server/controller/ActionResponseTest.java   |  46 +++
 .../AmbariManagementControllerTest.java         | 255 +++++++++++++--
 .../internal/AbstractResourceProviderTest.java  |  45 ---
 .../internal/ActionResourceProviderTest.java    | 321 ++++++++++++-------
 .../controller/internal/RequestImplTest.java    |   9 +-
 .../internal/RequestResourceProviderTest.java   | 142 +++++++-
 .../server/orm/dao/ActionDefinitionDAOTest.java | 137 ++++++++
 ambari-web/app/controllers/main/host/details.js |   7 +-
 ambari-web/app/utils/ajax.js                    |   6 +-
 42 files changed, 2724 insertions(+), 675 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/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
new file mode 100644
index 0000000..fdd57dd
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionDefinition.java
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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/incubator-ambari/blob/8b0e64c5/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 381c041..6b32e73 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
@@ -21,6 +21,7 @@ import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 import com.google.inject.persist.UnitOfWork;
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.agent.ActionQueue;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.controller.HostsMap;
@@ -42,23 +43,25 @@ import java.util.concurrent.atomic.AtomicLong;
  */
 @Singleton
 public class ActionManager {
+  private static Logger LOG = LoggerFactory.getLogger(ActionManager.class);
   private final ActionScheduler scheduler;
   private final ActionDBAccessor db;
   private final ActionQueue actionQueue;
-  private static Logger LOG = LoggerFactory.getLogger(ActionManager.class);
   private final AtomicLong requestCounter;
+  private final CustomActionDBAccessor cdb;
 
   @Inject
   public ActionManager(@Named("schedulerSleeptime") long schedulerSleepTime,
-      @Named("actionTimeout") long actionTimeout,
-      ActionQueue aq, Clusters fsm, ActionDBAccessor db, HostsMap hostsMap,
-      ServerActionManager serverActionManager, UnitOfWork unitOfWork) {
+                       @Named("actionTimeout") long actionTimeout,
+                       ActionQueue aq, Clusters fsm, ActionDBAccessor db, HostsMap hostsMap,
+                       ServerActionManager serverActionManager, UnitOfWork unitOfWork, CustomActionDBAccessor cdb) {
     this.actionQueue = aq;
     this.db = db;
     scheduler = new ActionScheduler(schedulerSleepTime, actionTimeout, db,
         actionQueue, fsm, 2, hostsMap, serverActionManager, unitOfWork);
     requestCounter = new AtomicLong(
         db.getLastPersistedRequestIdWhenInitialized());
+    this.cdb = cdb;
   }
 
   public void start() {
@@ -94,8 +97,8 @@ public class ActionManager {
   /**
    * Get all actions(stages) for a request.
    *
-   * @param requestId  the request id
-   * @return  list of all stages associated with the given request id
+   * @param requestId the request id
+   * @return list of all stages associated with the given request id
    */
   public List<Stage> getActions(long requestId) {
     return db.getAllStages(requestId);
@@ -114,7 +117,7 @@ public class ActionManager {
         LOG.debug("Processing command report : " + report.toString());
       }
       String actionId = report.getActionId();
-      long [] requestStageIds = StageUtils.getRequestStage(actionId);
+      long[] requestStageIds = StageUtils.getRequestStage(actionId);
       long requestId = requestStageIds[0];
       long stageId = requestStageIds[1];
       HostRoleCommand command = db.getTask(report.getTaskId());
@@ -167,6 +170,7 @@ public class ActionManager {
 
   /**
    * Returns last 20 requests
+   *
    * @return
    */
   public List<Long> getRequests() {
@@ -175,6 +179,7 @@ public class ActionManager {
 
   /**
    * Returns last 20 requests
+   *
    * @return
    */
   public List<Long> getRequestsByStatus(RequestStatus status) {
@@ -188,4 +193,35 @@ 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/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionType.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionType.java
new file mode 100644
index 0000000..6a57ce1
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ActionType.java
@@ -0,0 +1,30 @@
+/**
+ * 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;
+
+/**
+ * Defines various action types
+ */
+public enum ActionType {
+  DISABLED, // Action cannot be executed
+  SYSTEM, // System defined
+  USER, // User defined
+  SYSTEM_REQUIRES_ADMIN, // Requires admin privileges
+  USER_REQUIRES_ADMIN, // Requires admin privileges
+  SYSTEM_DISABLED; // Ambari has disabled the action
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/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
new file mode 100644
index 0000000..b861e76
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessor.java
@@ -0,0 +1,82 @@
+/**
+ * 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/incubator-ambari/blob/8b0e64c5/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
new file mode 100644
index 0000000..eb0cfa9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/CustomActionDBAccessorImpl.java
@@ -0,0 +1,222 @@
+/**
+ * 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);
+    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)
+      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 (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/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/TargetHostType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/TargetHostType.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/TargetHostType.java
new file mode 100644
index 0000000..c983859
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/TargetHostType.java
@@ -0,0 +1,28 @@
+/**
+ * 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;
+
+/**
+ * An enum describing how to select a host from a group of hosts eligible for action execution
+ */
+public enum TargetHostType {
+  ANY, // Any host
+  ALL, // On all hosts
+  MAJORITY, // On majority of the hosts
+  SPECIFIC; // On specific hosts specified during invocation
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/api/services/ActionService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ActionService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ActionService.java
index 8b8d0cc..78e8e07 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ActionService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ActionService.java
@@ -15,10 +15,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -26,109 +32,109 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+import java.util.Collections;
 
-import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.apache.ambari.server.controller.spi.Resource;
-
-import java.util.HashMap;
-import java.util.Map;
 
+/**
+ * Service responsible for action definition resource requests.
+ */
+@Path("/actions/")
 public class ActionService extends BaseService {
-  /**
-   * Parent cluster name.
-   */
-  private String m_clusterName;
-  
-  private String m_serviceName;
 
   /**
-   * Constructor.
+   * Handles: GET /actions/{actionName}
+   * Get a specific action definition.
    *
-   * @param clusterName cluster id
-   * @param serviceName service
+   * @param headers     http headers
+   * @param ui          uri info
+   * @param actionName action name
+   * @return action definition instance representation
    */
-  public ActionService(String clusterName, String serviceName) {
-    m_clusterName = clusterName;
-    m_serviceName = serviceName;
+  @GET
+  @Path("{actionName}")
+  @Produces("text/plain")
+  public Response getActionDefinition(@Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("actionName") String actionName) {
+
+    return handleRequest(headers, null, ui, Request.Type.GET, createActionDefinitionResource(actionName));
   }
 
   /**
-   * Handles URL: /clusters/{clusterId}/services/{serviceName}/actions
-   * Get all actions for a service in a cluster.
+   * Handles: GET  /actions
+   * Get all action definitions.
    *
    * @param headers http headers
    * @param ui      uri info
-   * @return service collection resource representation
+   * @return action definition collection resource representation
    */
   @GET
   @Produces("text/plain")
-  public Response getActions(@Context HttpHeaders headers, @Context UriInfo ui) {
-    return handleRequest(headers, null, ui, Request.Type.GET,
-        createActionResource(m_clusterName, m_serviceName, null));
+  public Response getActionDefinitions(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createActionDefinitionResource(null));
   }
 
   /**
-   * Handles URL: /clusters/{clusterId}/services/{serviceName}/actions.  
-   * The body should contain:
-   * <pre>
-   * {
-   *     "actionName":"name_string",
-   *     "parameters":
-   *     {
-   *         "key1":"value1",
-   *         // ...
-   *         "keyN":"valueN"
-   *     }
-   * }
-   * </pre>
-   * Get all services for a cluster.
+   * Handles: POST /actions/{actionName}
+   * Create a specific action definition.
    *
-   * @param headers http headers
-   * @param ui      uri info
-   * @return service collection resource representation
+   * @param headers     http headers
+   * @param ui          uri info
+   * @param actionName  action name
+   * @return information regarding the action definition being created
    */
-  @POST
-  @Produces("text/plain")
-  public Response createActions(String body,@Context HttpHeaders headers, @Context UriInfo ui) {
-    return handleRequest(headers, body, ui, Request.Type.POST,
-        createActionResource(m_clusterName, m_serviceName, null));
+   @POST
+   @Path("{actionName}")
+   @Produces("text/plain")
+   public Response createActionDefinition(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                                 @PathParam("actionName") String actionName) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST, createActionDefinitionResource(actionName));
   }
-  
+
   /**
-   * Handles: POST /clusters/{clusterId}/services/{serviceId}/{actionName}
-   * Create a specific action.
+   * Handles: PUT /actions/{actionName}
+   * Update a specific action definition.
    *
-   * @param body        http body
    * @param headers     http headers
    * @param ui          uri info
    * @param actionName  action name
-   *
-   * @return information regarding the created action
+   * @return information regarding the updated action
    */
-  @POST
+  @PUT
   @Path("{actionName}")
   @Produces("text/plain")
-  public Response createAction(String body, @Context HttpHeaders headers, @Context UriInfo ui,
-                               @PathParam("actionName") String actionName) {
-    return handleRequest(headers, body, ui, Request.Type.POST,
-        createActionResource(m_clusterName, m_serviceName, actionName));
+  public Response updateActionDefinition(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                                @PathParam("actionName") String actionName) {
+
+    return handleRequest(headers, body, ui, Request.Type.PUT, createActionDefinitionResource(actionName));
   }
 
   /**
-   * Create an action resource instance.
+   * Handles: DELETE /actions/{actionName}
+   * Delete a specific action definition.
    *
-   * @param clusterName cluster name
-   * @param serviceName service name
+   * @param headers     http headers
+   * @param ui          uri info
    * @param actionName  action name
-   *
-   * @return an action resource instance
+   * @return information regarding the deleted action definition
    */
-  ResourceInstance createActionResource(String clusterName, String serviceName, String actionName) {
-    Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>();
-    mapIds.put(Resource.Type.Cluster, clusterName);
-    mapIds.put(Resource.Type.Service, serviceName);
-    mapIds.put(Resource.Type.Action, actionName);
+  @DELETE
+  @Path("{actionName}")
+  @Produces("text/plain")
+  public Response deleteActionDefinition(@Context HttpHeaders headers, @Context UriInfo ui,
+                                @PathParam("actionName") String actionName) {
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createActionDefinitionResource(actionName));
+  }
 
-    return createResource(Resource.Type.Action, mapIds);
+  /**
+   * Create a action definition resource instance.
+   *
+   * @param actionName action name
+   *
+   * @return a action definition resource instance
+   */
+  ResourceInstance createActionDefinitionResource(String actionName) {
+    return createResource(Resource.Type.Action,
+        Collections.singletonMap(Resource.Type.Action, actionName));
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestService.java
index 5594050..4ed3a9d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestService.java
@@ -23,6 +23,7 @@ import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.controller.spi.Resource;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -98,6 +99,22 @@ public class RequestService extends BaseService {
   }
 
   /**
+   * Handles: POST /clusters/{clusterId}/requests
+   * Create multiple services.
+   *
+   * @param body        http body
+   * @param headers     http headers
+   * @param ui          uri info
+   * @return information regarding the created services
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createRequests(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST, createRequestResource(m_clusterName, null));
+  }
+
+  /**
    * Create a request resource instance.
    *
    * @param clusterName  cluster name

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
index da834a5..066002e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
@@ -179,17 +179,6 @@ public class ServiceService extends BaseService {
 
     return new ComponentService(m_clusterName, serviceName);
   }
-  
-  /**
-   * Get the components sub-resource.
-   *
-   * @param serviceName service id
-   * @return the action service
-   */
-  @Path("{serviceName}/actions")
-  public ActionService getActionHandler(@PathParam("serviceName") String serviceName) {
-    return new ActionService(m_clusterName, serviceName);
-  }
 
   /**
    * Create a service resource instance.

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionRequest.java
index 775382d..6fdac2f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ActionRequest.java
@@ -15,56 +15,120 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ambari.server.controller;
 
-import java.util.Map;
+package org.apache.ambari.server.controller;
 
+/**
+ * Used to perform CRUD operations of Action
+ */
 public class ActionRequest {
-  private String clusterName; 
 
-  private String serviceName;
-  
-  private String actionName; //for CREATE only
+  private String actionName;  //CRUD
+  private String actionType;  //C
+  private String inputs;  //C
+  private String targetService;  //C
+  private String targetComponent;  //C
+  private String description;  //CU
+  private String targetType;  //CU
+  private String defaultTimeout;  //CU
 
-  private Map<String, String> parameters; //for CREATE only
+  public ActionRequest(
+      String actionName, String actionType, String inputs,
+      String targetService, String targetComponent, String description, String targetType,
+      String defaultTimeout) {
+    setActionName(actionName);
+    setActionType(actionType);
+    setInputs(inputs);
+    setTargetService(targetService);
+    setTargetComponent(targetComponent);
+    setDescription(description);
+    setTargetType(targetType);
+    setDefaultTimeout(defaultTimeout);
+  }
+
+  /**
+   * Create the request to get all defined actions
+   *
+   * @return
+   */
+  public static ActionRequest getAllRequest() {
+    return new ActionRequest(null, null, null, null, null, null, null, null);
+  }
 
-  public ActionRequest(String clusterName, String serviceName,
-      String actionName, Map<String, String> params) {
-    this.clusterName = clusterName;
-    this.serviceName = serviceName;
+  public String getActionName() {
+    return actionName;
+  }
+
+  public void setActionName(String actionName) {
     this.actionName = actionName;
-    this.parameters = params;
   }
 
-  public String getClusterName() {
-    return clusterName;
+  public String getActionType() {
+    return actionType;
   }
 
-  public void setClusterName(String clusterName) {
-    this.clusterName = clusterName;
+  public void setActionType(String actionType) {
+    this.actionType = actionType;
   }
 
-  public String getServiceName() {
-    return serviceName;
+  public String getInputs() {
+    return inputs;
   }
 
-  public void setServiceName(String serviceName) {
-    this.serviceName = serviceName;
+  public void setInputs(String inputs) {
+    this.inputs = inputs;
   }
 
-  public String getActionName() {
-    return actionName;
+  public String getTargetService() {
+    return targetService;
   }
 
-  public void setActionName(String actionName) {
-    this.actionName = actionName;
+  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 String getTargetType() {
+    return targetType;
+  }
+
+  public void setTargetType(String targetType) {
+    this.targetType = targetType;
+  }
+
+  public String getDefaultTimeout() {
+    return defaultTimeout;
   }
 
-  public Map<String, String> getParameters() {
-    return parameters;
+  public void setDefaultTimeout(String defaultTimeout) {
+    this.defaultTimeout = defaultTimeout;
   }
 
-  public void setParameters(Map<String, String> parameters) {
-    this.parameters = parameters;
+  @Override
+  public String toString() {
+    return (new StringBuilder()).
+        append("actionName :" + actionName).
+        append(", actionType :" + actionType).
+        append(", inputs :" + inputs).
+        append(", targetService :" + targetService).
+        append(", targetComponent :" + targetComponent).
+        append(", description :" + description).
+        append(", targetType :" + targetType).
+        append(", defaultTimeout :" + defaultTimeout).toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/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 83d1a71..f85f640 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
@@ -15,30 +15,45 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.ambari.server.controller;
 
+import org.apache.ambari.server.actionmanager.ActionDefinition;
 
+/**
+ * Used to respond to GET requests for actions
+ */
 public class ActionResponse {
-  private String clusterName; 
-
-  private String serviceName;
   
   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 getClusterName() {
-    return clusterName;
+  public ActionResponse(String actionName, String actionType, String inputs,
+      String targetService, String targetComponent, String description, String targetType,
+      String defaultTimeout) {
+    setActionName(actionName);
+    setActionType(actionType);
+    setInputs(inputs);
+    setTargetService(targetService);
+    setTargetComponent(targetComponent);
+    setDescription(description);
+    setTargetType(targetType);
+    setDefaultTimeout(defaultTimeout);
   }
 
-  public void setClusterName(String clusterName) {
-    this.clusterName = clusterName;
-  }
-
-  public String getServiceName() {
-    return serviceName;
-  }
-
-  public void setServiceName(String serviceName) {
-    this.serviceName = serviceName;
+  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() {
@@ -48,4 +63,124 @@ public class ActionResponse {
   public void setActionName(String actionName) {
     this.actionName = actionName;
   }
+
+  public String getActionType() {
+    return actionType;
+  }
+
+  public void setActionType(String 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 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;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ActionResponse that = (ActionResponse) o;
+
+    if (actionName != null ?
+        !actionName.equals(that.actionName) : that.actionName != null) {
+      return false;
+    }
+
+    if (actionType != null ?
+        !actionType.equals(that.actionType) : that.actionType != null) {
+      return false;
+    }
+
+    if (description != null ?
+        !description.equals(that.description) : that.description != null) {
+      return false;
+    }
+
+    if (inputs != null ?
+        !inputs.equals(that.inputs) : that.inputs != null) {
+      return false;
+    }
+
+    if (targetService != null ?
+        !targetService.equals(that.targetService) : that.targetService != null) {
+      return false;
+    }
+
+    if (targetComponent != null ?
+        !targetComponent.equals(that.targetComponent) : that.targetComponent != null) {
+      return false;
+    }
+
+    if (targetType != null ?
+        !targetType.equals(that.targetType) : that.targetType != null) {
+      return false;
+    }
+
+    if (defaultTimeout != null ?
+        !defaultTimeout.equals(that.defaultTimeout) : that.defaultTimeout != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = 1;
+    result = 31 + (actionName != null ? actionName.hashCode() : 0);
+    result = result + (actionType != null ? actionType.hashCode() : 0);
+    result = result + (inputs != null ? inputs.hashCode() : 0);
+    result = result + (description != null ? description.hashCode() : 0);
+    result = result + (targetService != null ? targetService.hashCode() : 0);
+    result = result + (targetComponent != null ? targetComponent.hashCode() : 0);
+    result = result + (targetType != null ? targetType.hashCode() : 0);
+    result = result + (defaultTimeout != null ? defaultTimeout.hashCode() : 0);
+    return result;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
index cc869a3..67b2475 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
@@ -228,27 +228,15 @@ public interface AmbariManagementController {
   /**
    * Create the action defined by the attributes in the given request object.
    *
-   * @param request the request object which defines the action to be created
+   * @param actionRequest the request object which defines the action to be created
    * @param requestProperties the request properties
    *
    * @throws AmbariException thrown if the action cannot be created
    */
-  public RequestStatusResponse createActions(Set<ActionRequest> request, Map<String, String> requestProperties)
+  public RequestStatusResponse createAction(ExecuteActionRequest actionRequest, Map<String, String> requestProperties)
       throws AmbariException;
   
   /**
-   * Get the actions identified by the given request objects.
-   *
-   * @param request  the request objects which identify the actions to be returned
-   *
-   * @return a set of actions responses
-   *
-   * @throws AmbariException thrown if the resource cannot be read
-   */
-  public Set<ActionResponse> getActions(Set<ActionRequest> request)
-      throws AmbariException;
-
-  /**
    * Get supported stacks.
    * 
    * @param requests the stacks

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 6f28798..7146b0c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -45,11 +45,15 @@ import org.apache.ambari.server.ServiceComponentHostNotFoundException;
 import org.apache.ambari.server.ServiceComponentNotFoundException;
 import org.apache.ambari.server.ServiceNotFoundException;
 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.ActionType;
+import org.apache.ambari.server.actionmanager.CustomActionDBAccessorImpl;
 import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
 import org.apache.ambari.server.actionmanager.HostRoleCommand;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
+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;
@@ -2063,32 +2067,6 @@ public class AmbariManagementControllerImpl implements
     }
   }
 
-  @Override
-  public Set<ActionResponse> getActions(Set<ActionRequest> request)
-      throws AmbariException {
-    Set<ActionResponse> responses = new HashSet<ActionResponse>();
-
-    for (ActionRequest actionRequest : request) {
-      if (actionRequest.getServiceName() == null) {
-        LOG.warn("No service name specified - skipping request");
-        //TODO throw error?
-        continue;
-      }
-      ActionResponse actionResponse = new ActionResponse();
-      actionResponse.setClusterName(actionRequest.getClusterName());
-      actionResponse.setServiceName(actionRequest.getServiceName());
-      if (actionMetadata.getActions(actionRequest.getServiceName()) != null
-          && !actionMetadata.getActions(actionRequest.getServiceName())
-              .isEmpty()) {
-        actionResponse.setActionName(actionMetadata.getActions(
-            actionRequest.getServiceName()).get(0));
-      }
-      responses.add(actionResponse);
-    }
-
-    return responses;
-  }
-
   /**
    * Get a request response for the given request ids.  Note that this method
    * fully populates a request resource including the set of task sub-resources
@@ -2309,7 +2287,7 @@ public class AmbariManagementControllerImpl implements
     return hostName;
   }
 
-  private void addServiceCheckAction(ActionRequest actionRequest, Stage stage)
+  private void addServiceCheckAction(ExecuteActionRequest actionRequest, Stage stage)
       throws AmbariException {
     String clusterName = actionRequest.getClusterName();
     String componentName = actionMetadata.getClient(actionRequest
@@ -2351,12 +2329,12 @@ public class AmbariManagementControllerImpl implements
     }
 
     stage.addHostRoleExecutionCommand(hostName, Role.valueOf(actionRequest
-        .getActionName()), RoleCommand.EXECUTE,
+        .getCommandName()), RoleCommand.EXECUTE,
         new ServiceComponentHostOpInProgressEvent(componentName, hostName,
             System.currentTimeMillis()), clusterName, actionRequest
             .getServiceName());
 
-    stage.getExecutionCommandWrapper(hostName, actionRequest.getActionName()).getExecutionCommand()
+    stage.getExecutionCommandWrapper(hostName, actionRequest.getCommandName()).getExecutionCommand()
         .setRoleParams(actionRequest.getParameters());
 
     Cluster cluster = clusters.getCluster(clusterName);
@@ -2367,7 +2345,7 @@ public class AmbariManagementControllerImpl implements
       cluster, actionRequest.getServiceName(), hostName);
 
     ExecutionCommand execCmd = stage.getExecutionCommandWrapper(hostName,
-      actionRequest.getActionName()).getExecutionCommand();
+      actionRequest.getCommandName()).getExecutionCommand();
 
     execCmd.setConfigurations(configurations);
     execCmd.setConfigurationTags(configTags);
@@ -2383,7 +2361,7 @@ public class AmbariManagementControllerImpl implements
   }
 
   private void addDecommissionDatanodeAction(
-      ActionRequest decommissionRequest, Stage stage)
+      ExecuteActionRequest decommissionRequest, Stage stage)
       throws AmbariException {
     String hdfsExcludeFileType = "hdfs-exclude-file";
     // Find hdfs admin host, just decommission from namenode.
@@ -2403,11 +2381,16 @@ public class AmbariManagementControllerImpl implements
 
     if (excludeFileTag == null) {
       throw new IllegalArgumentException("No exclude file specified"
-          + " when decommissioning datanodes");
+          + " when decommissioning datanodes. Provide parameter excludeFileTag with the tag for config type "
+          + hdfsExcludeFileType);
     }
 
     Config config = clusters.getCluster(clusterName).getConfig(
         hdfsExcludeFileType, excludeFileTag);
+    if(config == null){
+      throw new AmbariException("Decommissioning datanodes requires the cluster to be associated with config type " +
+      hdfsExcludeFileType + " with a list of datanodes to be decommissioned (\"datanodes\" : list).");
+    }
 
     LOG.info("Decommissioning data nodes: " + config.getProperties().get("datanodes") +
         " " + hdfsExcludeFileType + " tag: " + excludeFileTag);
@@ -2447,7 +2430,7 @@ public class AmbariManagementControllerImpl implements
   }
 
   @Override
-  public RequestStatusResponse createActions(Set<ActionRequest> request, Map<String, String> requestProperties)
+  public RequestStatusResponse createAction(ExecuteActionRequest actionRequest, Map<String, String> requestProperties)
       throws AmbariException {
     String clusterName = null;
 
@@ -2463,41 +2446,40 @@ public class AmbariManagementControllerImpl implements
 
     String logDir = ""; //TODO empty for now
 
-    for (ActionRequest actionRequest : request) {
-      if (actionRequest.getClusterName() == null
-          || actionRequest.getClusterName().isEmpty()
-          || actionRequest.getServiceName() == null
-          || actionRequest.getServiceName().isEmpty()
-          || actionRequest.getActionName() == null
-          || actionRequest.getActionName().isEmpty()) {
-        throw new AmbariException("Invalid action request : " + "cluster="
-            + actionRequest.getClusterName() + ", service="
-            + actionRequest.getServiceName() + ", action="
-            + actionRequest.getActionName());
-      } else if (clusterName == null) {
-        clusterName = actionRequest.getClusterName();
-      } else if (!clusterName.equals(actionRequest.getClusterName())) {
-        throw new AmbariException("Requests for different clusters found");
-      }
+    if (actionRequest.getClusterName() == null
+        || actionRequest.getClusterName().isEmpty()
+        || actionRequest.getServiceName() == null
+        || actionRequest.getServiceName().isEmpty()
+        || actionRequest.getCommandName() == null
+        || actionRequest.getCommandName().isEmpty()) {
+      throw new AmbariException("Invalid action request : " + "cluster="
+          + actionRequest.getClusterName() + ", service="
+          + actionRequest.getServiceName() + ", command="
+          + actionRequest.getCommandName());
     }
 
+    clusterName = actionRequest.getClusterName();
+
     Stage stage = stageFactory.createNew(actionManager.getNextRequestId(),
         logDir, clusterName, requestContext);
 
     stage.setStageId(0);
-    for (ActionRequest actionRequest : request) {
-      LOG.info("Received a createAction request"
-          + ", clusterName=" + actionRequest.getClusterName()
-          + ", serviceName=" + actionRequest.getServiceName()
-          + ", request=" + actionRequest.toString());
-
-      if (actionRequest.getActionName().contains("SERVICE_CHECK")) {
-        addServiceCheckAction(actionRequest, stage);
-      } else if (actionRequest.getActionName().equals("DECOMMISSION_DATANODE")) {
-        addDecommissionDatanodeAction(actionRequest, stage);
-      } else {
-        throw new AmbariException("Unsupported action");
-      }
+    LOG.info("Received a createAction request"
+        + ", clusterName=" + actionRequest.getClusterName()
+        + ", serviceName=" + actionRequest.getServiceName()
+        + ", request=" + actionRequest.toString());
+
+    if (!isValidCommand(actionRequest.getCommandName(), actionRequest.getServiceName())) {
+      throw new AmbariException(
+          "Unsupported action " + actionRequest.getCommandName() + " for " + actionRequest.getServiceName());
+    }
+
+    if (actionRequest.getCommandName().contains("SERVICE_CHECK")) {
+      addServiceCheckAction(actionRequest, stage);
+    } else if (actionRequest.getCommandName().equals("DECOMMISSION_DATANODE")) {
+      addDecommissionDatanodeAction(actionRequest, stage);
+    } else {
+      throw new AmbariException("Unsupported action " + actionRequest.getCommandName());
     }
 
     Cluster cluster = clusters.getCluster(clusterName);
@@ -2532,6 +2514,19 @@ public class AmbariManagementControllerImpl implements
 
   }
 
+  private Boolean isValidCommand(String command, String service) {
+    List<String> actions = actionMetadata.getActions(service);
+    if (actions == null || actions.size() == 0) {
+      return false;
+    }
+
+    if (!actions.contains(command)) {
+      return false;
+    }
+
+    return true;
+  }
+
   private Set<StackResponse> getStacks(StackRequest request)
       throws AmbariException {
     Set<StackResponse> response;

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/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 1b2ebda..0859322 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
@@ -24,6 +24,8 @@ import java.util.Properties;
 
 import org.apache.ambari.server.actionmanager.ActionDBAccessor;
 import org.apache.ambari.server.actionmanager.ActionDBAccessorImpl;
+import org.apache.ambari.server.actionmanager.CustomActionDBAccessor;
+import org.apache.ambari.server.actionmanager.CustomActionDBAccessorImpl;
 import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
 import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
 import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
@@ -96,6 +98,7 @@ public class ControllerModule extends AbstractModule {
     bind(Gson.class).in(Scopes.SINGLETON);
     bind(Clusters.class).to(ClustersImpl.class);
     bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class);
+    bind(CustomActionDBAccessor.class).to(CustomActionDBAccessorImpl.class);
     bindConstant().annotatedWith(Names.named("schedulerSleeptime")).to(10000L);
     bindConstant().annotatedWith(Names.named("actionTimeout")).to(600000L);
 

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/controller/ExecuteActionRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ExecuteActionRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ExecuteActionRequest.java
new file mode 100644
index 0000000..fd1cd1f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ExecuteActionRequest.java
@@ -0,0 +1,89 @@
+/**
+ * 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.controller;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class to capture details used to create action or custom commands
+ */
+public class ExecuteActionRequest {
+  private String clusterName;
+  private String commandName;
+  private String actionName;
+  private String serviceName;
+  private String componentName;
+  private List<String> hosts;
+  private Map<String, String> parameters;
+
+  public ExecuteActionRequest(String clusterName, String commandName,
+                       String actionName, String serviceName, String componentName,
+                       List<String> hosts, Map<String, String> parameters) {
+    this.clusterName = clusterName;
+    this.commandName = commandName;
+    this.actionName = actionName;
+    this.serviceName = serviceName;
+    this.componentName = componentName;
+    this.parameters = parameters;
+    this.hosts = hosts;
+  }
+
+  /**
+   * Create an ExecuteActionRequest to execute a command
+   */
+  public ExecuteActionRequest(String clusterName, String commandName, String serviceName,
+                       Map<String, String> parameters) {
+    this.clusterName = clusterName;
+    this.commandName = commandName;
+    this.serviceName = serviceName;
+    this.parameters = parameters;
+  }
+
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public String getCommandName() {
+    return commandName;
+  }
+
+  public String getActionName() {
+    return actionName;
+  }
+
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  public String getComponentName() {
+    return componentName;
+  }
+
+  public Map<String, String> getParameters() {
+    return parameters;
+  }
+
+  public List<String> getHosts() {
+    return hosts;
+  }
+
+  public Boolean isCommand() {
+    return actionName == null || actionName.isEmpty();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/8b0e64c5/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 f3df656..54ab41c 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
@@ -15,126 +15,306 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+
 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.controller.ActionRequest;
+import org.apache.ambari.server.controller.ActionResponse;
 import org.apache.ambari.server.controller.AmbariManagementController;
-import org.apache.ambari.server.controller.spi.*;
 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;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.Resource.Type;
+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 java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 
-/**
- * Resource provider for action resources.
- */
-class ActionResourceProvider extends AbstractControllerResourceProvider {
-
-  // ----- Property ID constants ---------------------------------------------
-
-  // Actions
-  protected static final String ACTION_CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Actions", "cluster_name");
-  protected static final String ACTION_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Actions", "service_name");
-  protected static final String ACTION_ACTION_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Actions", "action_name");
+public class ActionResourceProvider extends AbstractControllerResourceProvider {
 
+  public static final String ACTION_NAME_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "action_name");
+  public static final String ACTION_TYPE_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "action_type");
+  public static final String INPUTS_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "inputs");
+  public static final String TARGET_SERVICE_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "target_service");
+  public static final String TARGET_COMPONENT_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "target_component");
+  public static final String DESCRIPTION_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "description");
+  public static final String TARGET_HOST_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "target_type");
+  public static final String DEFAULT_TIMEOUT_PROPERTY_ID = PropertyHelper
+      .getPropertyId("Actions", "default_timeout");
+  private static Set<String> pkPropertyIds = new HashSet<String>(
+      Arrays.asList(new String[]{ACTION_NAME_PROPERTY_ID}));
+  private Boolean enableExperimental = false;
 
-  private static Set<String> pkPropertyIds =
-      new HashSet<String>(Arrays.asList(new String[]{
-          ACTION_CLUSTER_NAME_PROPERTY_ID,
-          ACTION_SERVICE_NAME_PROPERTY_ID}));
+  public ActionResourceProvider(Set<String> propertyIds,
+                                Map<Type, String> keyPropertyIds,
+                                AmbariManagementController managementController) {
+    super(propertyIds, keyPropertyIds, managementController);
+  }
 
-  ActionResourceProvider(Set<String> propertyIds,
-                         Map<Resource.Type, String> keyPropertyIds,
-                         AmbariManagementController managementController) {
+  public Boolean getEnableExperimental() {
+    return enableExperimental;
+  }
 
-    super(propertyIds, keyPropertyIds, managementController);
+  public void setEnableExperimental(Boolean enabled) {
+    enableExperimental = enabled;
   }
 
   @Override
   public RequestStatus createResources(Request request)
       throws SystemException,
-             UnsupportedPropertyException,
-             ResourceAlreadyExistsException,
-             NoSuchParentResourceException {
+      UnsupportedPropertyException,
+      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());
+
+          createActionDefinition(actionReq);
+          return null;
+        }
+      });
+    }
+    notifyCreate(Type.Action, request);
+
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public RequestStatus updateResources(final Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
+
+    if (!getEnableExperimental()) {
+      throw new UnsupportedOperationException("Not currently supported.");
+    }
 
     final Set<ActionRequest> requests = new HashSet<ActionRequest>();
-    
-    final Map<String, String> requestInfoProperties = request.getRequestInfoProperties();
-    
-    for (Map<String, Object> propertyMap : request.getProperties()) {
-      requests.add(getRequest(propertyMap));
+    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);
+      }
     }
-    return getRequestStatus(createResources(new Command<RequestStatusResponse>() {
+    response = modifyResources(new Command<RequestStatusResponse>() {
       @Override
       public RequestStatusResponse invoke() throws AmbariException {
-        return getManagementController().createActions(requests, requestInfoProperties);
+        return updateActionDefinitions(requests, request.getRequestInfoProperties());
       }
-    }));
+    });
+    notifyUpdate(Type.Action, request, predicate);
+    return getRequestStatus(response);
   }
 
   @Override
   public Set<Resource> getResources(Request request, Predicate predicate)
-      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
-    throw new UnsupportedOperationException("Not currently supported.");
-  }
+      throws SystemException, UnsupportedPropertyException,
+      NoSuchResourceException, NoSuchParentResourceException {
 
-  @Override
-  public RequestStatus updateResources(Request request, Predicate predicate)
-      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
-    throw new UnsupportedOperationException("Not currently supported.");
+    final Set<ActionRequest> requests = new HashSet<ActionRequest>();
+    if (predicate != null) {
+      for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
+        ActionRequest actionReq = getRequest(propertyMap);
+        LOG.debug("Received a get request for Action with"
+            + ", actionName = " + actionReq.getActionName());
+        requests.add(actionReq);
+      }
+    } else {
+      LOG.debug("Received a get request for all Actions");
+      requests.add(ActionRequest.getAllRequest());
+    }
+
+    Set<ActionResponse> responses = getResources(new Command<Set<ActionResponse>>() {
+      @Override
+      public Set<ActionResponse> invoke() throws AmbariException {
+        return getActionDefinitions(requests);
+      }
+    });
+
+    Set<String> requestedIds = getRequestPropertyIds(request, predicate);
+    Set<Resource> resources = new HashSet<Resource>();
+
+    for (ActionResponse response : responses) {
+      Resource resource = new ResourceImpl(Type.Action);
+      setResourceProperty(resource, ACTION_NAME_PROPERTY_ID,
+          response.getActionName(), requestedIds);
+      setResourceProperty(resource, ACTION_TYPE_PROPERTY_ID,
+          response.getActionType(), requestedIds);
+      setResourceProperty(resource, INPUTS_PROPERTY_ID,
+          response.getInputs(), requestedIds);
+      setResourceProperty(resource, TARGET_SERVICE_PROPERTY_ID,
+          response.getTargetService(), requestedIds);
+      setResourceProperty(resource, TARGET_COMPONENT_PROPERTY_ID,
+          response.getTargetComponent(), requestedIds);
+      setResourceProperty(resource, DESCRIPTION_PROPERTY_ID,
+          response.getDescription(), requestedIds);
+      setResourceProperty(resource, TARGET_HOST_PROPERTY_ID,
+          response.getTargetType(), requestedIds);
+      setResourceProperty(resource, DEFAULT_TIMEOUT_PROPERTY_ID,
+          response.getDefaultTimeout(), requestedIds);
+      resources.add(resource);
+    }
+    return resources;
   }
 
   @Override
   public RequestStatus deleteResources(Predicate predicate)
       throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
-    throw new UnsupportedOperationException("Not currently supported.");
-  }
-
-  @Override
-  public Set<String> checkPropertyIds(Set<String> propertyIds) {
-    propertyIds = super.checkPropertyIds(propertyIds);
 
-    if (propertyIds.isEmpty()) {
-      return propertyIds;
+    if (!getEnableExperimental()) {
+      throw new UnsupportedOperationException("Not currently supported.");
     }
-    Set<String> unsupportedProperties = new HashSet<String>();
 
-    for (String propertyId : propertyIds) {
-      String propertyCategory = PropertyHelper.getPropertyCategory(propertyId);
-      if (propertyCategory == null || !propertyCategory.equals("parameters")) {
-        unsupportedProperties.add(propertyId);
+    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());
       }
     }
-    return unsupportedProperties;
+    notifyDelete(Type.Action, predicate);
+    return getRequestStatus(null);
+  }
+
+  private ActionRequest getRequest(Map<String, Object> properties) {
+    ActionRequest ar = new ActionRequest(
+        (String) properties.get(ACTION_NAME_PROPERTY_ID),
+        (String) properties.get(ACTION_TYPE_PROPERTY_ID),
+        (String) properties.get(INPUTS_PROPERTY_ID),
+        (String) properties.get(TARGET_SERVICE_PROPERTY_ID),
+        (String) properties.get(TARGET_COMPONENT_PROPERTY_ID),
+        (String) properties.get(DESCRIPTION_PROPERTY_ID),
+        (String) properties.get(TARGET_HOST_PROPERTY_ID),
+        (String) properties.get(DEFAULT_TIMEOUT_PROPERTY_ID));
+
+    return ar;
   }
 
   @Override
-  protected Set<String> getPKPropertyIds() {
+  public Set<String> getPKPropertyIds() {
     return pkPropertyIds;
   }
 
-  private ActionRequest getRequest(Map<String, Object> properties) {
-    Map<String, String> params = new HashMap<String, String>();
-    for (Entry<String, Object> entry : properties.entrySet()) {
-      String propertyid = entry.getKey();
-
-      String propertyCategory = PropertyHelper.getPropertyCategory(propertyid);
-      if (propertyCategory != null &&
-          propertyCategory.equals("parameters") &&
-          null != entry.getValue()) {
-        params.put(PropertyHelper.getPropertyName(propertyid), entry.getValue().toString());
+  private ActionManager getActionManager() {
+    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);
+  }
+
+  protected synchronized Set<ActionResponse> getActionDefinitions(Set<ActionRequest> requests)
+      throws AmbariException {
+    Set<ActionResponse> responses = new HashSet<ActionResponse>();
+    for (ActionRequest request : requests) {
+      if (request.getActionName() == null) {
+        List<ActionDefinition> ads = getActionManager().getAllActionDefinition();
+        for (ActionDefinition ad : ads) {
+          responses.add(new ActionResponse(ad));
+        }
+      } else {
+        ActionDefinition ad = getActionManager().getActionDefinition(request.getActionName());
+        if (ad != null) {
+          responses.add(new ActionResponse(ad));
+        }
       }
     }
-    return new ActionRequest(
-        (String)  properties.get(ACTION_CLUSTER_NAME_PROPERTY_ID),
-        (String)  properties.get(ACTION_SERVICE_NAME_PROPERTY_ID),
-        (String)  properties.get(ACTION_ACTION_NAME_PROPERTY_ID),
-        params);
+
+    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/incubator-ambari/blob/8b0e64c5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
index dc5afab..fce29fe 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
@@ -141,7 +141,7 @@ public class RequestImpl implements Request {
     for (Map<String, Object> map : properties) {
       for (Entry<String, Object> entry : map.entrySet()) {
         sb.append(" { propertyName=").append(entry.getKey()).append(", propertyValue=").
-            append(entry.getValue().toString()).append(" }, ");
+            append(entry.getValue()==null?"NULL":entry.getValue().toString()).append(" }, ");
       }
     }
     sb.append(" ], temporalInfo=[");
@@ -151,7 +151,7 @@ public class RequestImpl implements Request {
       for (Entry<String, TemporalInfo> entry :
         m_mapTemporalInfo.entrySet()) {
         sb.append(" { propertyName=").append(entry.getKey()).append(", temporalInfo=").
-            append(entry.getValue().toString());
+            append(entry.getValue()==null?"NULL":entry.getValue().toString());
       }
     }
     sb.append(" ]");


Mime
View raw message