org.apache.ambari.server.controller.AmbariManagementControllerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ambari.server.controller.AmbariManagementControllerTest.java

Source

/**
 * 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 static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.persistence.EntityManager;

import junit.framework.Assert;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ClusterNotFoundException;
import org.apache.ambari.server.DuplicateResourceException;
import org.apache.ambari.server.HostNotFoundException;
import org.apache.ambari.server.ObjectNotFoundException;
import org.apache.ambari.server.ParentObjectNotFoundException;
import org.apache.ambari.server.Role;
import org.apache.ambari.server.RoleCommand;
import org.apache.ambari.server.ServiceNotFoundException;
import org.apache.ambari.server.StackAccessException;
import org.apache.ambari.server.actionmanager.ActionDBAccessor;
import org.apache.ambari.server.actionmanager.ActionType;
import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
import org.apache.ambari.server.actionmanager.HostRoleCommand;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.actionmanager.Request;
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.controller.internal.ComponentResourceProviderTest;
import org.apache.ambari.server.controller.internal.HostComponentResourceProviderTest;
import org.apache.ambari.server.controller.internal.HostResourceProviderTest;
import org.apache.ambari.server.controller.internal.RequestOperationLevel;
import org.apache.ambari.server.controller.internal.RequestResourceFilter;
import org.apache.ambari.server.controller.internal.ServiceResourceProviderTest;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.customactions.ActionDefinition;
import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
import org.apache.ambari.server.metadata.ActionMetadata;
import org.apache.ambari.server.orm.GuiceJpaInitializer;
import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
import org.apache.ambari.server.orm.OrmTestHelper;
import org.apache.ambari.server.orm.dao.ExecutionCommandDAO;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.entities.ExecutionCommandEntity;
import org.apache.ambari.server.security.authorization.Users;
import org.apache.ambari.server.serveraction.ServerAction;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.Config;
import org.apache.ambari.server.state.ConfigFactory;
import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.ConfigImpl;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.HostComponentAdminState;
import org.apache.ambari.server.state.HostState;
import org.apache.ambari.server.state.MaintenanceState;
import org.apache.ambari.server.state.RepositoryInfo;
import org.apache.ambari.server.state.RepositoryVersionState;
import org.apache.ambari.server.state.SecurityType;
import org.apache.ambari.server.state.Service;
import org.apache.ambari.server.state.ServiceComponent;
import org.apache.ambari.server.state.ServiceComponentFactory;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.ServiceComponentHostFactory;
import org.apache.ambari.server.state.ServiceFactory;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.ServiceOsSpecific;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.State;
import org.apache.ambari.server.state.configgroup.ConfigGroup;
import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostInstallEvent;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostOpSucceededEvent;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartedEvent;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStopEvent;
import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStoppedEvent;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.collections.CollectionUtils;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.persist.PersistService;

public class AmbariManagementControllerTest {

    private static final Logger LOG = LoggerFactory.getLogger(AmbariManagementControllerTest.class);

    private static final String STACK_NAME = "HDP";

    private static final String STACK_VERSION = "0.2";
    private static final String NEW_STACK_VERSION = "2.0.6";
    private static final String OS_TYPE = "centos5";
    private static final String REPO_ID = "HDP-1.1.1.16";
    private static final String PROPERTY_NAME = "hbase.regionserver.msginterval";
    private static final String SERVICE_NAME = "HDFS";
    private static final String FAKE_SERVICE_NAME = "FAKENAGIOS";
    private static final int STACK_VERSIONS_CNT = 14;
    private static final int REPOS_CNT = 3;
    private static final int STACKS_CNT = 3;
    private static final int STACK_PROPERTIES_CNT = 103;
    private static final int STACK_COMPONENTS_CNT = 4;
    private static final int OS_CNT = 2;

    private static final String NON_EXT_VALUE = "XXX";
    private static final String INCORRECT_BASE_URL = "http://incorrect.url";

    private static final String COMPONENT_NAME = "NAMENODE";

    private static final String REQUEST_CONTEXT_PROPERTY = "context";

    private static final String CLUSTER_HOST_INFO = "clusterHostInfo";

    private AmbariManagementController controller;
    private Clusters clusters;
    private ActionDBAccessor actionDB;
    private Injector injector;
    private ServiceFactory serviceFactory;
    private ServiceComponentFactory serviceComponentFactory;
    private ServiceComponentHostFactory serviceComponentHostFactory;
    private AmbariMetaInfo ambariMetaInfo;
    private Users users;
    private EntityManager entityManager;
    private Properties backingProperties;
    private Configuration configuration;
    private ConfigHelper configHelper;
    private ConfigGroupFactory configGroupFactory;
    private OrmTestHelper helper;

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    @Before
    public void setup() throws Exception {
        InMemoryDefaultTestModule module = new InMemoryDefaultTestModule();
        backingProperties = module.getProperties();
        injector = Guice.createInjector(module);
        injector.getInstance(GuiceJpaInitializer.class);
        entityManager = injector.getInstance(EntityManager.class);
        clusters = injector.getInstance(Clusters.class);
        actionDB = injector.getInstance(ActionDBAccessor.class);
        controller = injector.getInstance(AmbariManagementController.class);
        serviceFactory = injector.getInstance(ServiceFactory.class);
        serviceComponentFactory = injector.getInstance(ServiceComponentFactory.class);
        serviceComponentHostFactory = injector.getInstance(ServiceComponentHostFactory.class);
        ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
        ambariMetaInfo.init();
        users = injector.getInstance(Users.class);
        configuration = injector.getInstance(Configuration.class);
        configHelper = injector.getInstance(ConfigHelper.class);
        configGroupFactory = injector.getInstance(ConfigGroupFactory.class);
        helper = injector.getInstance(OrmTestHelper.class);
    }

    @After
    public void teardown() {
        injector.getInstance(PersistService.class).stop();
        actionDB = null;
    }

    private void setOsFamily(Host host, String osFamily, String osVersion) {
        Map<String, String> hostAttributes = new HashMap<String, String>();
        hostAttributes.put("os_family", osFamily);
        hostAttributes.put("os_release_version", osVersion);

        host.setHostAttributes(hostAttributes);
    }

    private void addHost(String hostname) throws AmbariException {
        addHost(hostname, null);
    }

    private void addHost(String hostname, String clusterName) throws AmbariException {
        clusters.addHost(hostname);
        setOsFamily(clusters.getHost(hostname), "redhat", "6.3");
        clusters.getHost(hostname).setState(HostState.HEALTHY);
        clusters.getHost(hostname).persist();
        if (null != clusterName) {
            clusters.mapHostToCluster(hostname, clusterName);
        }
    }

    private void deleteHost(String hostname) throws AmbariException {
        clusters.deleteHost(hostname);
    }

    /**
     * Creates a Cluster object, along with its corresponding ClusterVersion based on the stack.
     * @param clusterName Cluster name
     * @throws AmbariException
     */
    private void createCluster(String clusterName) throws AmbariException {
        ClusterRequest r = new ClusterRequest(null, clusterName, State.INSTALLED.name(), SecurityType.NONE,
                "HDP-0.1", null);
        controller.createCluster(r);
    }

    private void createService(String clusterName, String serviceName, State desiredState) throws AmbariException {
        String dStateStr = null;
        if (desiredState != null) {
            dStateStr = desiredState.toString();
        }
        ServiceRequest r1 = new ServiceRequest(clusterName, serviceName, dStateStr);
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r1);

        ServiceResourceProviderTest.createServices(controller, requests);
    }

    private void createServiceComponent(String clusterName, String serviceName, String componentName,
            State desiredState) throws AmbariException {
        String dStateStr = null;
        if (desiredState != null) {
            dStateStr = desiredState.toString();
        }
        ServiceComponentRequest r = new ServiceComponentRequest(clusterName, serviceName, componentName, dStateStr);
        Set<ServiceComponentRequest> requests = new HashSet<ServiceComponentRequest>();
        requests.add(r);
        ComponentResourceProviderTest.createComponents(controller, requests);
    }

    private void createServiceComponentHost(String clusterName, String serviceName, String componentName,
            String hostname, State desiredState) throws AmbariException {
        String dStateStr = null;
        if (desiredState != null) {
            dStateStr = desiredState.toString();
        }
        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, serviceName, componentName,
                hostname, dStateStr);
        Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
        requests.add(r);
        controller.createHostComponents(requests);
    }

    private void deleteServiceComponentHost(String clusterName, String serviceName, String componentName,
            String hostname, State desiredState) throws AmbariException {
        String dStateStr = null;
        if (desiredState != null) {
            dStateStr = desiredState.toString();
        }
        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, serviceName, componentName,
                hostname, dStateStr);
        Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
        requests.add(r);
        controller.deleteHostComponents(requests);
    }

    private Long createConfigGroup(Cluster cluster, String name, String tag, List<String> hosts,
            List<Config> configs) throws AmbariException {

        Map<String, Host> hostMap = new HashMap<String, Host>();
        Map<String, Config> configMap = new HashMap<String, Config>();

        for (String hostname : hosts) {
            Host host = clusters.getHost(hostname);
            hostMap.put(host.getHostName(), host);
        }

        for (Config config : configs) {
            configMap.put(config.getType(), config);
        }

        ConfigGroup configGroup = configGroupFactory.createNew(cluster, name, tag, "", configMap, hostMap);

        configGroup.persist();
        cluster.addConfigGroup(configGroup);

        return configGroup.getId();
    }

    private long stopService(String clusterName, String serviceName, boolean runSmokeTests,
            boolean reconfigureClients) throws AmbariException {
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);
        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");
        RequestStatusResponse resp = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, runSmokeTests, reconfigureClients);

        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // manually change live state to stopped as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        return resp.getRequestId();
    }

    private long stopServiceComponentHosts(String clusterName, String serviceName) throws Exception {
        Cluster c = clusters.getCluster(clusterName);
        Service s = c.getService(serviceName);
        Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
        for (ServiceComponent sc : s.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                ServiceComponentHostRequest schr = new ServiceComponentHostRequest(clusterName, serviceName,
                        sc.getName(), sch.getHostName(), State.INSTALLED.name());
                requests.add(schr);
            }
        }
        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");
        RequestStatusResponse resp = HostComponentResourceProviderTest.updateHostComponents(controller, injector,
                requests, mapRequestProps, false);

        // manually change live state to started as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }
        return resp.getRequestId();
    }

    private long startService(String clusterName, String serviceName, boolean runSmokeTests,
            boolean reconfigureClients) throws AmbariException {
        return startService(clusterName, serviceName, runSmokeTests, reconfigureClients, null);
    }

    private long startService(String clusterName, String serviceName, boolean runSmokeTests,
            boolean reconfigureClients, MaintenanceStateHelper maintenanceStateHelper) throws AmbariException {
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.STARTED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);
        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");
        RequestStatusResponse resp = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, runSmokeTests, reconfigureClients, maintenanceStateHelper);

        Assert.assertEquals(State.STARTED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        if (resp != null) {
            // manually change live state to stopped as no running action manager
            List<HostRoleCommand> commands = actionDB.getRequestTasks(resp.getRequestId());
            for (HostRoleCommand cmd : commands) {
                String scName = cmd.getRole().toString();
                if (!scName.endsWith("CHECK")) {
                    Cluster cluster = clusters.getCluster(clusterName);
                    String hostname = cmd.getHostName();
                    for (Service s : cluster.getServices().values()) {
                        if (s.getServiceComponents().containsKey(scName)
                                && !s.getServiceComponent(scName).isClientComponent()) {
                            s.getServiceComponent(scName).getServiceComponentHost(hostname).setState(State.STARTED);
                            break;
                        }
                    }
                }
            }
            return resp.getRequestId();
        } else {
            return -1;
        }
    }

    private long installService(String clusterName, String serviceName, boolean runSmokeTests,
            boolean reconfigureClients) throws AmbariException {
        return installService(clusterName, serviceName, runSmokeTests, reconfigureClients, null);
    }

    /**
     * Allows to set maintenanceStateHelper. For use when there is anything to test
     * with maintenance mode.
     */
    private long installService(String clusterName, String serviceName, boolean runSmokeTests,
            boolean reconfigureClients, MaintenanceStateHelper maintenanceStateHelper) throws AmbariException {
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);
        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");
        RequestStatusResponse resp = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, runSmokeTests, reconfigureClients, maintenanceStateHelper);

        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        if (resp != null) {
            // manually change live state to stopped as no running action manager
            List<HostRoleCommand> commands = actionDB.getRequestTasks(resp.getRequestId());
            for (HostRoleCommand cmd : commands) {
                clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(cmd.getRole().name())
                        .getServiceComponentHost(cmd.getHostName()).setState(State.INSTALLED);
            }
            return resp.getRequestId();
        } else {
            return -1;
        }
    }

    private boolean checkExceptionType(Throwable e, Class<? extends Exception> exceptionClass) {
        return e != null && (exceptionClass.isAssignableFrom(e.getClass())
                || checkExceptionType(e.getCause(), exceptionClass));
    }

    @Test
    public void testCreateClusterSimple() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Set<ClusterResponse> r = controller
                .getClusters(Collections.singleton(new ClusterRequest(null, clusterName, null, null)));
        Assert.assertEquals(1, r.size());
        ClusterResponse c = r.iterator().next();
        Assert.assertEquals(clusterName, c.getClusterName());

        try {
            createCluster(clusterName);
            fail("Duplicate cluster creation should fail");
        } catch (AmbariException e) {
            // Expected
        }
    }

    @Test
    public void testCreateClusterWithInvalidStack() {
        // TODO implement test after meta data integration
    }

    @Test
    public void testCreateClusterWithHostMapping() throws AmbariException {
        Set<String> hostNames = new HashSet<String>();
        hostNames.add("h1");
        hostNames.add("h2");
        ClusterRequest r = new ClusterRequest(null, "c1", "HDP-0.1", hostNames);

        try {
            controller.createCluster(r);
            fail("Expected create cluster to fail for invalid hosts");
        } catch (Exception e) {
            // Expected
        }

        try {
            clusters.getCluster("c1");
            fail("Expected to fail for non created cluster");
        } catch (ClusterNotFoundException e) {
            // Expected
        }

        clusters.addHost("h1");
        clusters.addHost("h2");
        setOsFamily(clusters.getHost("h1"), "redhat", "6.3");
        setOsFamily(clusters.getHost("h2"), "redhat", "6.3");
        clusters.getHost("h1").persist();
        clusters.getHost("h2").persist();

        controller.createCluster(r);
        Assert.assertNotNull(clusters.getCluster("c1"));
    }

    @Test
    public void testCreateClusterWithDesiredClusterConfigs() {
        // TODO implement after configs integration
    }

    @Test
    public void testCreateClusterWithInvalidRequest() {
        ClusterRequest r = new ClusterRequest(null, null, null, null);
        r.toString();

        try {
            controller.createCluster(r);
            fail("Expected create cluster for invalid request");
        } catch (Exception e) {
            // Expected
        }

        r.setClusterId(1L);
        try {
            controller.createCluster(r);
            fail("Expected create cluster for invalid request");
        } catch (Exception e) {
            // Expected
        }
        r.setClusterId(null);

        r.setClusterName("foo");
        try {
            controller.createCluster(r);
            fail("Expected create cluster for invalid request - no stack version");
        } catch (Exception e) {
            // Expected
        }

        r.setStackVersion("HDP-1.0.1");
        r.setProvisioningState(State.INSTALLING.name());
        try {
            controller.createCluster(r);
            fail("Expected create cluster for invalid request - invalid provisioning state");
        } catch (Exception e) {
            // Expected
        }
    }

    @Test
    public void testCreateServicesSimple() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName = "HDFS";
        clusters.getCluster("foo1").setDesiredStackVersion(new StackId("HDP-0.1"));
        createService(clusterName, serviceName, State.INIT);

        Service s = clusters.getCluster(clusterName).getService(serviceName);
        Assert.assertNotNull(s);
        Assert.assertEquals(serviceName, s.getName());
        Assert.assertEquals(clusterName, s.getCluster().getClusterName());

        ServiceRequest req = new ServiceRequest(clusterName, serviceName, null);

        Set<ServiceResponse> r = ServiceResourceProviderTest.getServices(controller, Collections.singleton(req));
        Assert.assertEquals(1, r.size());
        ServiceResponse resp = r.iterator().next();
        Assert.assertEquals(serviceName, resp.getServiceName());
        Assert.assertEquals(clusterName, resp.getClusterName());
        Assert.assertEquals(State.INIT.toString(), resp.getDesiredState());
        Assert.assertEquals("HDP-0.1", resp.getDesiredStackVersion());

        // TODO test resp.getConfigVersions()
    }

    @Test
    public void testCreateServicesWithInvalidRequest() throws AmbariException {
        // invalid request
        // dups in requests
        // multi cluster updates

        Set<ServiceRequest> set1 = new HashSet<ServiceRequest>();

        try {
            set1.clear();
            ServiceRequest rInvalid = new ServiceRequest(null, null, null);
            set1.add(rInvalid);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceRequest rInvalid = new ServiceRequest("foo", null, null);
            set1.add(rInvalid);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceRequest rInvalid = new ServiceRequest("foo", "bar", null);
            set1.add(rInvalid);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid cluster");
        } catch (AmbariException e) {
            // Expected
            Assert.assertTrue(checkExceptionType(e, ParentObjectNotFoundException.class));
        }

        clusters.addCluster("foo");
        clusters.addCluster("bar");
        clusters.getCluster("foo").setDesiredStackVersion(new StackId("HDP-0.1"));
        clusters.getCluster("bar").setDesiredStackVersion(new StackId("HDP-0.1"));

        try {
            set1.clear();
            ServiceRequest valid1 = new ServiceRequest("foo", "HDFS", null);
            ServiceRequest valid2 = new ServiceRequest("foo", "HDFS", null);
            set1.add(valid1);
            set1.add(valid2);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceRequest valid1 = new ServiceRequest("foo", "bar", null);
            set1.add(valid1);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid service");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceRequest valid1 = new ServiceRequest("foo", "HDFS", null);
            ServiceRequest valid2 = new ServiceRequest("bar", "HDFS", null);
            set1.add(valid1);
            set1.add(valid2);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for multiple clusters");
        } catch (Exception e) {
            // Expected
        }

        Assert.assertNotNull(clusters.getCluster("foo"));
        Assert.assertEquals(0, clusters.getCluster("foo").getServices().size());

        set1.clear();
        ServiceRequest valid = new ServiceRequest("foo", "HDFS", null);
        set1.add(valid);
        ServiceResourceProviderTest.createServices(controller, set1);

        try {
            set1.clear();
            ServiceRequest valid1 = new ServiceRequest("foo", "HDFS", null);
            ServiceRequest valid2 = new ServiceRequest("foo", "HDFS", null);
            set1.add(valid1);
            set1.add(valid2);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for existing service");
        } catch (Exception e) {
            // Expected
        }

        Assert.assertEquals(1, clusters.getCluster("foo").getServices().size());

    }

    @Test
    public void testCreateServiceWithInvalidInfo() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName = "HDFS";
        try {
            createService(clusterName, serviceName, State.INSTALLING);
            fail("Service creation should fail for invalid state");
        } catch (Exception e) {
            // Expected
        }
        try {
            clusters.getCluster(clusterName).getService(serviceName);
            fail("Service creation should have failed");
        } catch (Exception e) {
            // Expected
        }
        try {
            createService(clusterName, serviceName, State.INSTALLED);
            fail("Service creation should fail for invalid initial state");
        } catch (Exception e) {
            // Expected
        }

        createService(clusterName, serviceName, null);

        String serviceName2 = "MAPREDUCE";
        createService(clusterName, serviceName2, State.INIT);

        ServiceRequest r = new ServiceRequest(clusterName, null, null);
        Set<ServiceResponse> response = ServiceResourceProviderTest.getServices(controller,
                Collections.singleton(r));
        Assert.assertEquals(2, response.size());

        for (ServiceResponse svc : response) {
            Assert.assertTrue(
                    svc.getServiceName().equals(serviceName) || svc.getServiceName().equals(serviceName2));
            Assert.assertEquals("HDP-0.1", svc.getDesiredStackVersion());
            Assert.assertEquals(State.INIT.toString(), svc.getDesiredState());
        }
    }

    @Test
    public void testCreateServicesMultiple() throws AmbariException {
        Set<ServiceRequest> set1 = new HashSet<ServiceRequest>();
        clusters.addCluster("foo");
        clusters.getCluster("foo").setDesiredStackVersion(new StackId("HDP-0.1"));

        ServiceRequest valid1 = new ServiceRequest("foo", "HDFS", null);
        ServiceRequest valid2 = new ServiceRequest("foo", "MAPREDUCE", null);
        set1.add(valid1);
        set1.add(valid2);
        ServiceResourceProviderTest.createServices(controller, set1);

        try {
            valid1 = new ServiceRequest("foo", "PIG", null);
            valid2 = new ServiceRequest("foo", "MAPREDUCE", null);
            set1.add(valid1);
            set1.add(valid2);
            ServiceResourceProviderTest.createServices(controller, set1);
            fail("Expected failure for invalid services");
        } catch (AmbariException e) {
            // Expected
            Assert.assertTrue(checkExceptionType(e, DuplicateResourceException.class));
        }

        Assert.assertNotNull(clusters.getCluster("foo"));
        Assert.assertEquals(2, clusters.getCluster("foo").getServices().size());
        Assert.assertNotNull(clusters.getCluster("foo").getService("HDFS"));
        Assert.assertNotNull(clusters.getCluster("foo").getService("MAPREDUCE"));
    }

    @Test
    public void testCreateServiceComponentSimple() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);

        String componentName = "NAMENODE";
        try {
            createServiceComponent(clusterName, serviceName, componentName, State.INSTALLING);
            fail("ServiceComponent creation should fail for invalid state");
        } catch (Exception e) {
            // Expected
        }
        try {
            clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(componentName);
            fail("ServiceComponent creation should have failed");
        } catch (Exception e) {
            // Expected
        }

        createServiceComponent(clusterName, serviceName, componentName, State.INIT);
        Assert.assertNotNull(
                clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(componentName));

        ServiceComponentRequest r = new ServiceComponentRequest(clusterName, serviceName, null, null);
        Set<ServiceComponentResponse> response = ComponentResourceProviderTest.getComponents(controller,
                Collections.singleton(r));
        Assert.assertEquals(1, response.size());

        ServiceComponentResponse sc = response.iterator().next();
        Assert.assertEquals(State.INIT.toString(), sc.getDesiredState());
        Assert.assertEquals(componentName, sc.getComponentName());
        Assert.assertEquals(clusterName, sc.getClusterName());
        Assert.assertEquals(serviceName, sc.getServiceName());
    }

    @Test
    public void testCreateServiceComponentWithInvalidRequest() throws AmbariException {
        // multiple clusters
        // dup objects
        // existing components
        // invalid request params
        // invalid service
        // invalid cluster

        Set<ServiceComponentRequest> set1 = new HashSet<ServiceComponentRequest>();

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest(null, null, null, null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest("c1", null, null, null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest("c1", "s1", null, null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for invalid requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest("c1", "s1", "sc1", null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for invalid cluster");
        } catch (ParentObjectNotFoundException e) {
            // Expected
        }

        clusters.addCluster("c1");
        clusters.addCluster("c2");

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for invalid service");
        } catch (ParentObjectNotFoundException e) {
            // Expected
        }

        Cluster c1 = clusters.getCluster("c1");
        StackId stackId = new StackId("HDP-0.1");
        c1.setDesiredStackVersion(stackId);
        helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
        c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);
        Service s1 = serviceFactory.createNew(c1, "HDFS");
        Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
        c1.addService(s1);
        c1.addService(s2);
        s1.persist();
        s2.persist();

        set1.clear();
        ServiceComponentRequest valid1 = new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null);
        ServiceComponentRequest valid2 = new ServiceComponentRequest("c1", "MAPREDUCE", "JOBTRACKER", null);
        ServiceComponentRequest valid3 = new ServiceComponentRequest("c1", "MAPREDUCE", "TASKTRACKER", null);
        set1.add(valid1);
        set1.add(valid2);
        set1.add(valid3);
        ComponentResourceProviderTest.createComponents(controller, set1);

        try {
            set1.clear();
            ServiceComponentRequest rInvalid1 = new ServiceComponentRequest("c1", "HDFS", "HDFS_CLIENT", null);
            ServiceComponentRequest rInvalid2 = new ServiceComponentRequest("c1", "HDFS", "HDFS_CLIENT", null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for dups in requests");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentRequest rInvalid1 = new ServiceComponentRequest("c1", "HDFS", "HDFS_CLIENT", null);
            ServiceComponentRequest rInvalid2 = new ServiceComponentRequest("c2", "HDFS", "HDFS_CLIENT", null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for multiple clusters");
        } catch (Exception e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentRequest rInvalid = new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null);
            set1.add(rInvalid);
            ComponentResourceProviderTest.createComponents(controller, set1);
            fail("Expected failure for already existing component");
        } catch (Exception e) {
            // Expected
        }

        Assert.assertEquals(1, s1.getServiceComponents().size());
        Assert.assertNotNull(s1.getServiceComponent("NAMENODE"));
        Assert.assertEquals(2, s2.getServiceComponents().size());
        Assert.assertNotNull(s2.getServiceComponent("JOBTRACKER"));
        Assert.assertNotNull(s2.getServiceComponent("TASKTRACKER"));

    }

    @Test
    public void testCreateServiceComponentWithConfigs() {
        // FIXME after config impl
    }

    @Test
    public void testCreateServiceComponentMultiple() throws AmbariException {
        clusters.addCluster("c1");
        clusters.addCluster("c2");

        Cluster c1 = clusters.getCluster("c1");
        StackId stackId = new StackId("HDP-0.2");
        c1.setDesiredStackVersion(stackId);
        helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
        c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);

        Service s1 = serviceFactory.createNew(c1, "HDFS");
        Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
        c1.addService(s1);
        c1.addService(s2);
        s1.persist();
        s2.persist();

        Set<ServiceComponentRequest> set1 = new HashSet<ServiceComponentRequest>();
        ServiceComponentRequest valid1 = new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null);
        ServiceComponentRequest valid2 = new ServiceComponentRequest("c1", "MAPREDUCE", "JOBTRACKER", null);
        ServiceComponentRequest valid3 = new ServiceComponentRequest("c1", "MAPREDUCE", "TASKTRACKER", null);
        set1.add(valid1);
        set1.add(valid2);
        set1.add(valid3);
        ComponentResourceProviderTest.createComponents(controller, set1);

        Assert.assertEquals(1, c1.getService("HDFS").getServiceComponents().size());
        Assert.assertEquals(2, c1.getService("MAPREDUCE").getServiceComponents().size());
        Assert.assertNotNull(c1.getService("HDFS").getServiceComponent("NAMENODE"));
        Assert.assertNotNull(c1.getService("MAPREDUCE").getServiceComponent("JOBTRACKER"));
        Assert.assertNotNull(c1.getService("MAPREDUCE").getServiceComponent("TASKTRACKER"));
    }

    @Test
    public void testCreateServiceComponentHostSimple() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        try {
            createServiceComponentHost(clusterName, serviceName, componentName1, host1, State.INIT);
            fail("ServiceComponentHost creation should fail for invalid host" + " as host not mapped to cluster");
        } catch (Exception e) {
            // Expected
        }

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        try {
            createServiceComponentHost(clusterName, serviceName, componentName1, host1, State.INSTALLING);
            fail("ServiceComponentHost creation should fail for invalid state");
        } catch (Exception e) {
            // Expected
        }

        try {
            clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(componentName1)
                    .getServiceComponentHost(host1);
            fail("ServiceComponentHost creation should have failed earlier");
        } catch (Exception e) {
            // Expected
        }

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        try {
            createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
            fail("ServiceComponentHost creation should fail as duplicate");
        } catch (Exception e) {
            // Expected
        }

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host2));

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, serviceName, componentName2,
                null, null);

        Set<ServiceComponentHostResponse> response = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, response.size());

    }

    @Test
    public void testCreateServiceComponentHostMultiple() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName, componentName2,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName, componentName1,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r4 = new ServiceComponentHostRequest(clusterName, serviceName, componentName2,
                host2, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        set1.add(r4);
        controller.createHostComponents(set1);

        Assert.assertEquals(2, clusters.getCluster(clusterName).getServiceComponentHosts(host1).size());
        Assert.assertEquals(2, clusters.getCluster(clusterName).getServiceComponentHosts(host2).size());

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
    }

    @Test
    public void testCreateServiceComponentHostWithInvalidRequest() throws AmbariException {
        // multiple clusters
        // dup objects
        // existing components
        // invalid request params
        // invalid service
        // invalid cluster
        // invalid component
        // invalid host

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest(null, null, null, null, null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid requests");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", null, null, null, null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid requests");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", null, null, null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid requests");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", null,
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid requests");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid cluster");
        } catch (ParentObjectNotFoundException e) {
            // Expected
        }

        clusters.addCluster("foo");
        clusters.addCluster("c1");
        clusters.addCluster("c2");
        Cluster foo = clusters.getCluster("foo");
        Cluster c1 = clusters.getCluster("c1");
        Cluster c2 = clusters.getCluster("c2");

        StackId stackId = new StackId("HDP-0.2");
        helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());

        foo.setDesiredStackVersion(stackId);
        foo.setCurrentStackVersion(stackId);
        foo.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);
        foo.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(),
                RepositoryVersionState.CURRENT);

        stackId = new StackId("HDP-0.2");
        c1.setDesiredStackVersion(stackId);
        c1.setCurrentStackVersion(stackId);
        c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);
        c1.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(),
                RepositoryVersionState.CURRENT);

        stackId = new StackId("HDP-0.2");
        c2.setDesiredStackVersion(stackId);
        c2.setCurrentStackVersion(stackId);
        c2.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);
        c2.transitionClusterVersion(stackId.getStackName(), stackId.getStackVersion(),
                RepositoryVersionState.CURRENT);

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid service");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        Service s1 = serviceFactory.createNew(foo, "HDFS");
        foo.addService(s1);
        s1.persist();
        Service s2 = serviceFactory.createNew(c1, "HDFS");
        c1.addService(s2);
        s2.persist();
        Service s3 = serviceFactory.createNew(c2, "HDFS");
        c2.addService(s3);
        s3.persist();

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid service");
        } catch (AmbariException e) {
            // Expected
        }

        ServiceComponent sc1 = serviceComponentFactory.createNew(s1, "NAMENODE");
        s1.addServiceComponent(sc1);
        sc1.persist();
        ServiceComponent sc2 = serviceComponentFactory.createNew(s2, "NAMENODE");
        s2.addServiceComponent(sc2);
        sc2.persist();
        ServiceComponent sc3 = serviceComponentFactory.createNew(s3, "NAMENODE");
        s3.addServiceComponent(sc3);
        sc3.persist();

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid host");
        } catch (AmbariException e) {
            // Expected
        }

        clusters.addHost("h1");
        Host h1 = clusters.getHost("h1");
        h1.setIPv4("ipv41");
        h1.setIPv6("ipv61");
        setOsFamily(h1, "redhat", "6.3");
        h1.persist();
        clusters.addHost("h2");
        Host h2 = clusters.getHost("h2");
        h2.setIPv4("ipv42");
        h2.setIPv6("ipv62");
        setOsFamily(h2, "redhat", "6.3");
        h2.persist();
        clusters.addHost("h3");
        Host h3 = clusters.getHost("h3");
        h3.setIPv4("ipv43");
        h3.setIPv6("ipv63");
        setOsFamily(h3, "redhat", "6.3");
        h3.persist();

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            set1.add(rInvalid);
            controller.createHostComponents(set1);
            fail("Expected failure for invalid host cluster mapping");
        } catch (AmbariException e) {
            // Expected
        }

        Set<String> hostnames = new HashSet<String>();
        hostnames.add("h1");
        hostnames.add("h2");
        hostnames.add("h3");
        clusters.mapHostsToCluster(hostnames, "foo");
        clusters.mapHostsToCluster(hostnames, "c1");
        clusters.mapHostsToCluster(hostnames, "c2");

        set1.clear();
        ServiceComponentHostRequest valid = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1", null);
        set1.add(valid);
        controller.createHostComponents(set1);

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid1 = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h2",
                    null);
            ServiceComponentHostRequest rInvalid2 = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h2",
                    null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            controller.createHostComponents(set1);
            fail("Expected failure for dup requests");
        } catch (DuplicateResourceException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid1 = new ServiceComponentHostRequest("c1", "HDFS", "NAMENODE", "h2",
                    null);
            ServiceComponentHostRequest rInvalid2 = new ServiceComponentHostRequest("c2", "HDFS", "NAMENODE", "h3",
                    null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            controller.createHostComponents(set1);
            fail("Expected failure for multiple clusters");
        } catch (IllegalArgumentException e) {
            // Expected
        }

        try {
            set1.clear();
            ServiceComponentHostRequest rInvalid1 = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h1",
                    null);
            ServiceComponentHostRequest rInvalid2 = new ServiceComponentHostRequest("foo", "HDFS", "NAMENODE", "h2",
                    null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            controller.createHostComponents(set1);
            fail("Expected failure for already existing");
        } catch (DuplicateResourceException e) {
            // Expected
        }

        Assert.assertEquals(1, foo.getServiceComponentHosts("h1").size());
        Assert.assertEquals(0, foo.getServiceComponentHosts("h2").size());
        Assert.assertEquals(0, foo.getServiceComponentHosts("h3").size());

        set1.clear();
        ServiceComponentHostRequest valid1 = new ServiceComponentHostRequest("c1", "HDFS", "NAMENODE", "h1", null);
        set1.add(valid1);
        controller.createHostComponents(set1);

        set1.clear();
        ServiceComponentHostRequest valid2 = new ServiceComponentHostRequest("c2", "HDFS", "NAMENODE", "h1", null);
        set1.add(valid2);
        controller.createHostComponents(set1);

        Assert.assertEquals(1, foo.getServiceComponentHosts("h1").size());
        Assert.assertEquals(1, c1.getServiceComponentHosts("h1").size());
        Assert.assertEquals(1, c2.getServiceComponentHosts("h1").size());

    }

    @Test
    public void testCreateHostSimple() throws AmbariException {
        Map<String, String> hostAttributes = null;

        HostRequest r1 = new HostRequest("h1", null, hostAttributes);
        r1.toString();

        Set<HostRequest> requests = new HashSet<HostRequest>();
        requests.add(r1);
        try {
            HostResourceProviderTest.createHosts(controller, requests);
            fail("Create host should fail for non-bootstrapped host");
        } catch (Exception e) {
            // Expected
        }

        clusters.addHost("h1");
        clusters.addHost("h2");
        setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
        setOsFamily(clusters.getHost("h2"), "redhat", "5.9");
        clusters.getHost("h1").persist();
        clusters.getHost("h2").persist();

        requests.add(new HostRequest("h2", "foo", new HashMap<String, String>()));

        try {
            HostResourceProviderTest.createHosts(controller, requests);
            fail("Create host should fail for invalid clusters");
        } catch (Exception e) {
            // Expected
        }

        clusters.addCluster("foo");
        Cluster c = clusters.getCluster("foo");
        StackId stackId = new StackId("HDP-0.1");
        c.setDesiredStackVersion(stackId);
        c.setCurrentStackVersion(stackId);
        helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
        c.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);

        HostResourceProviderTest.createHosts(controller, requests);

        Assert.assertNotNull(clusters.getHost("h1"));
        Assert.assertNotNull(clusters.getHost("h2"));

        Assert.assertEquals(0, clusters.getClustersForHost("h1").size());
        Assert.assertEquals(1, clusters.getClustersForHost("h2").size());

    }

    @Test
    public void testCreateHostMultiple() throws AmbariException {
        clusters.addHost("h1");
        clusters.addHost("h2");
        clusters.addHost("h3");
        clusters.addCluster("c1");
        Cluster c = clusters.getCluster("c1");
        StackId stackID = new StackId("HDP-0.1");
        c.setDesiredStackVersion(stackID);
        c.setCurrentStackVersion(stackID);
        helper.getOrCreateRepositoryVersion(stackID.getStackName(), stackID.getStackVersion());
        c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);

        setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
        setOsFamily(clusters.getHost("h2"), "redhat", "5.9");
        setOsFamily(clusters.getHost("h3"), "redhat", "5.9");
        clusters.getHost("h1").persist();
        clusters.getHost("h2").persist();
        clusters.getHost("h3").persist();

        String clusterName = "c1";

        HostRequest r1 = new HostRequest("h1", clusterName, null);
        HostRequest r2 = new HostRequest("h2", clusterName, null);
        HostRequest r3 = new HostRequest("h3", null, null);

        Set<HostRequest> set1 = new HashSet<HostRequest>();
        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        HostResourceProviderTest.createHosts(controller, set1);

        Assert.assertEquals(1, clusters.getClustersForHost("h1").size());
        Assert.assertEquals(1, clusters.getClustersForHost("h2").size());
        Assert.assertEquals(0, clusters.getClustersForHost("h3").size());
    }

    @Test
    public void testCreateHostWithInvalidRequests() throws AmbariException {
        // unknown host
        // invalid clusters
        // duplicate host

        Set<HostRequest> set1 = new HashSet<HostRequest>();

        try {
            set1.clear();
            HostRequest rInvalid = new HostRequest("h1", null, null);
            set1.add(rInvalid);
            HostResourceProviderTest.createHosts(controller, set1);
            fail("Expected failure for invalid host");
        } catch (Exception e) {
            // Expected
        }

        clusters.addHost("h1");

        String clusterName = "c1";

        try {
            set1.clear();
            HostRequest rInvalid = new HostRequest("h1", clusterName, null);
            set1.add(rInvalid);
            HostResourceProviderTest.createHosts(controller, set1);
            fail("Expected failure for invalid cluster");
        } catch (Exception e) {
            // Expected
        }

        clusters.addCluster("c1");

        try {
            set1.clear();
            HostRequest rInvalid1 = new HostRequest("h1", clusterName, null);
            HostRequest rInvalid2 = new HostRequest("h1", clusterName, null);
            set1.add(rInvalid1);
            set1.add(rInvalid2);
            HostResourceProviderTest.createHosts(controller, set1);
            fail("Expected failure for dup requests");
        } catch (Exception e) {
            // Expected
        }

    }

    @Test
    /**
     * Create a cluster with a service, and verify that the request tasks have the correct output log and error log paths.
     */
    public void testRequestStatusLogs() throws Exception {
        testCreateServiceComponentHostSimple();

        String clusterName = "foo1";
        String serviceName = "HDFS";

        Cluster cluster = clusters.getCluster(clusterName);
        for (Host h : clusters.getHosts()) {
            // Simulate each agent registering and setting the prefix path on its host
            h.setPrefix(Configuration.PREFIX_DIR);
        }

        Map<String, Config> configs = new HashMap<String, Config>();
        Map<String, String> properties = new HashMap<String, String>();
        Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String, String>>();

        Config c1 = new ConfigImpl(cluster, "hdfs-site", properties, propertiesAttributes, injector);
        c1.setTag("v1");
        cluster.addConfig(c1);
        c1.persist();
        configs.put(c1.getType(), c1);

        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, true, false);

        List<ShortTaskStatus> taskStatuses = trackAction.getTasks();
        Assert.assertFalse(taskStatuses.isEmpty());
        for (ShortTaskStatus task : taskStatuses) {
            Assert.assertEquals("Task output logs don't match",
                    Configuration.PREFIX_DIR + "/output-" + task.getTaskId() + ".txt", task.getOutputLog());
            Assert.assertEquals("Task error logs don't match",
                    Configuration.PREFIX_DIR + "/errors-" + task.getTaskId() + ".txt", task.getErrorLog());
        }
    }

    @Test
    public void testInstallAndStartService() throws Exception {
        testCreateServiceComponentHostSimple();

        String clusterName = "foo1";
        String serviceName = "HDFS";

        Cluster cluster = clusters.getCluster(clusterName);
        Service s1 = cluster.getService(serviceName);

        Map<String, Config> configs = new HashMap<String, Config>();
        Map<String, String> properties = new HashMap<String, String>();
        Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String, String>>();
        properties.put("a", "a1");
        properties.put("b", "b1");

        Config c1 = new ConfigImpl(cluster, "hdfs-site", properties, propertiesAttributes, injector);
        properties.put("c", "c1");
        properties.put("d", "d1");
        Config c2 = new ConfigImpl(cluster, "core-site", properties, propertiesAttributes, injector);
        Config c3 = new ConfigImpl(cluster, "foo-site", properties, propertiesAttributes, injector);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        c1.setTag("v1");
        c2.setTag("v1");
        c3.setTag("v1");

        cluster.addConfig(c1);
        cluster.addConfig(c2);
        cluster.addConfig(c3);
        c1.persist();
        c2.persist();
        c3.persist();

        configs.put(c1.getType(), c1);
        configs.put(c2.getType(), c2);

        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                Assert.assertEquals(State.INIT, sch.getState());
            }
        }

        List<ShortTaskStatus> taskStatuses = trackAction.getTasks();
        Assert.assertEquals(5, taskStatuses.size());

        boolean foundH1NN = false;
        boolean foundH1DN = false;
        boolean foundH2DN = false;
        boolean foundH1CLT = false;
        boolean foundH2CLT = false;

        for (ShortTaskStatus taskStatus : taskStatuses) {
            LOG.debug("Task dump :" + taskStatus.toString());
            Assert.assertEquals(RoleCommand.INSTALL.toString(), taskStatus.getCommand());
            Assert.assertEquals(HostRoleStatus.PENDING.toString(), taskStatus.getStatus());
            if (taskStatus.getHostName().equals("h1")) {
                if (Role.NAMENODE.toString().equals(taskStatus.getRole())) {
                    foundH1NN = true;
                } else if (Role.DATANODE.toString().equals(taskStatus.getRole())) {
                    foundH1DN = true;
                } else if (Role.HDFS_CLIENT.toString().equals(taskStatus.getRole())) {
                    foundH1CLT = true;
                } else {
                    fail("Found invalid role for host h1");
                }
            } else if (taskStatus.getHostName().equals("h2")) {
                if (Role.DATANODE.toString().equals(taskStatus.getRole())) {
                    foundH2DN = true;
                } else if (Role.HDFS_CLIENT.toString().equals(taskStatus.getRole())) {
                    foundH2CLT = true;
                } else {
                    fail("Found invalid role for host h2");
                }
            } else {
                fail("Found invalid host in task list");
            }
        }
        Assert.assertTrue(foundH1DN && foundH1NN && foundH2DN && foundH1CLT && foundH2CLT);

        // TODO validate stages?
        List<Stage> stages = actionDB.getAllStages(trackAction.getRequestId());
        Assert.assertEquals(1, stages.size());

        for (Stage stage : stages) {
            LOG.info("Stage Details for Install Service" + ", stageId=" + stage.getStageId() + ", actionId="
                    + stage.getActionId());

            for (String host : stage.getHosts()) {
                for (ExecutionCommandWrapper ecw : stage.getExecutionCommands(host)) {
                    Assert.assertFalse(ecw.getExecutionCommand().getHostLevelParams().get("repo_info").isEmpty());
                }
            }
        }

        Set<TaskStatusRequest> taskRequests = new HashSet<TaskStatusRequest>();
        TaskStatusRequest t1, t2;
        t1 = new TaskStatusRequest();
        t2 = new TaskStatusRequest();
        t1.setRequestId(trackAction.getRequestId());
        taskRequests.add(t1);
        Set<TaskStatusResponse> taskResponses = controller.getTaskStatus(taskRequests);
        Assert.assertEquals(5, taskResponses.size());

        t1.setTaskId(1L);
        t2.setRequestId(trackAction.getRequestId());
        t2.setTaskId(2L);
        taskRequests.clear();
        taskRequests.add(t1);
        taskRequests.add(t2);
        taskResponses = controller.getTaskStatus(taskRequests);
        Assert.assertEquals(2, taskResponses.size());

        // manually change live state to installed as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        r = new ServiceRequest(clusterName, serviceName, State.STARTED.toString());
        requests.clear();
        requests.add(r);
        trackAction = ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true,
                false);

        Assert.assertEquals(State.STARTED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            if (sc.getName().equals("HDFS_CLIENT")) {
                Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            } else {
                Assert.assertEquals(State.STARTED, sc.getDesiredState());
            }
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (sch.getServiceComponentName().equals("HDFS_CLIENT")) {
                    Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                } else {
                    Assert.assertEquals(State.STARTED, sch.getDesiredState());
                }
            }
        }

        // TODO validate stages?
        stages = actionDB.getAllStages(trackAction.getRequestId());
        Assert.assertEquals(2, stages.size());

        StringBuilder sb = new StringBuilder();
        clusters.debugDump(sb);
        LOG.info("Cluster Dump: " + sb.toString());

        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (sc.isClientComponent()) {
                    sch.setState(State.INSTALLED);
                } else {
                    sch.setState(State.INSTALL_FAILED);
                }
            }
        }

        r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        requests.clear();
        requests.add(r);
        trackAction = ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true,
                false);

        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
            }
        }

        // TODO validate stages?
        stages = actionDB.getAllStages(trackAction.getRequestId());

        Assert.assertEquals(1, stages.size());

    }

    @Test
    public void testGetClusters() throws AmbariException {
        clusters.addCluster("c1");

        Cluster c1 = clusters.getCluster("c1");

        StackId stackId = new StackId("HDP-0.1");
        c1.setDesiredStackVersion(stackId);
        helper.getOrCreateRepositoryVersion(stackId.getStackName(), stackId.getStackVersion());
        c1.createClusterVersion(stackId.getStackName(), stackId.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);

        ClusterRequest r = new ClusterRequest(null, null, null, null);
        Set<ClusterResponse> resp = controller.getClusters(Collections.singleton(r));
        Assert.assertEquals(1, resp.size());

        ClusterResponse resp1 = resp.iterator().next();

        Assert.assertEquals(c1.getClusterId(), resp1.getClusterId().longValue());
        Assert.assertEquals(c1.getClusterName(), resp1.getClusterName());
        Assert.assertEquals(c1.getDesiredStackVersion().getStackId(), resp1.getDesiredStackVersion());
    }

    @Test
    public void testGetClustersWithFilters() throws AmbariException {
        clusters.addCluster("c1");
        clusters.addCluster("c2");
        clusters.addCluster("c3");
        clusters.addCluster("c4");

        Cluster c1 = clusters.getCluster("c1");
        Cluster c2 = clusters.getCluster("c2");
        Cluster c3 = clusters.getCluster("c3");
        Cluster c4 = clusters.getCluster("c4");

        c1.setDesiredStackVersion(new StackId("HDP-0.1"));
        c2.setDesiredStackVersion(new StackId("HDP-0.1"));
        c3.setDesiredStackVersion(new StackId("HDP-1.1.0"));

        ClusterRequest r = new ClusterRequest(null, null, null, null);
        Set<ClusterResponse> resp = controller.getClusters(Collections.singleton(r));
        Assert.assertEquals(4, resp.size());

        r = new ClusterRequest(null, "c1", null, null);
        resp = controller.getClusters(Collections.singleton(r));
        Assert.assertEquals(1, resp.size());
        Assert.assertEquals(c1.getClusterId(), resp.iterator().next().getClusterId().longValue());

        r = new ClusterRequest(null, null, "HDP-0.1", null);
        resp = controller.getClusters(Collections.singleton(r));
        Assert.assertEquals(2, resp.size());

        r = new ClusterRequest(null, null, "", null);
        resp = controller.getClusters(Collections.singleton(r));
        Assert.assertEquals(1, resp.size());
        Assert.assertEquals(c4.getClusterId(), resp.iterator().next().getClusterId().longValue());
    }

    @Test
    public void testGetServices() throws AmbariException {
        clusters.addCluster("c1");
        Cluster c1 = clusters.getCluster("c1");
        c1.setDesiredStackVersion(new StackId("HDP-0.1"));
        Service s1 = serviceFactory.createNew(c1, "HDFS");

        c1.addService(s1);
        s1.setDesiredStackVersion(new StackId("HDP-0.1"));
        s1.setDesiredState(State.INSTALLED);

        s1.persist();

        ServiceRequest r = new ServiceRequest("c1", null, null);
        Set<ServiceResponse> resp = ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));

        ServiceResponse resp1 = resp.iterator().next();

        Assert.assertEquals(s1.getClusterId(), resp1.getClusterId().longValue());
        Assert.assertEquals(s1.getCluster().getClusterName(), resp1.getClusterName());
        Assert.assertEquals(s1.getName(), resp1.getServiceName());
        Assert.assertEquals("HDP-0.1", s1.getDesiredStackVersion().getStackId());
        Assert.assertEquals(s1.getDesiredStackVersion().getStackId(), resp1.getDesiredStackVersion());
        Assert.assertEquals(State.INSTALLED.toString(), resp1.getDesiredState());

    }

    @Test
    public void testGetServicesWithFilters() throws AmbariException {
        clusters.addCluster("c1");
        clusters.addCluster("c2");
        Cluster c1 = clusters.getCluster("c1");
        Cluster c2 = clusters.getCluster("c2");
        c1.setDesiredStackVersion(new StackId("HDP-0.2"));
        c2.setDesiredStackVersion(new StackId("HDP-0.2"));

        Service s1 = serviceFactory.createNew(c1, "HDFS");
        Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
        Service s3 = serviceFactory.createNew(c1, "HBASE");
        Service s4 = serviceFactory.createNew(c2, "HIVE");
        Service s5 = serviceFactory.createNew(c2, "ZOOKEEPER");

        c1.addService(s1);
        c1.addService(s2);
        c1.addService(s3);
        c2.addService(s4);
        c2.addService(s5);

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);
        s4.setDesiredState(State.INSTALLED);

        s1.persist();
        s2.persist();
        s3.persist();
        s4.persist();
        s5.persist();

        ServiceRequest r = new ServiceRequest(null, null, null);
        Set<ServiceResponse> resp;

        try {
            ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
            fail("Expected failure for invalid request");
        } catch (Exception e) {
            // Expected
        }

        r = new ServiceRequest(c1.getClusterName(), null, null);
        resp = ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
        Assert.assertEquals(3, resp.size());

        r = new ServiceRequest(c1.getClusterName(), s2.getName(), null);
        resp = ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
        Assert.assertEquals(1, resp.size());
        Assert.assertEquals(s2.getName(), resp.iterator().next().getServiceName());

        try {
            r = new ServiceRequest(c2.getClusterName(), s1.getName(), null);
            ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
            fail("Expected failure for invalid service");
        } catch (Exception e) {
            // Expected
        }

        r = new ServiceRequest(c1.getClusterName(), null, "INSTALLED");
        resp = ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
        Assert.assertEquals(2, resp.size());

        r = new ServiceRequest(c2.getClusterName(), null, "INIT");
        resp = ServiceResourceProviderTest.getServices(controller, Collections.singleton(r));
        Assert.assertEquals(1, resp.size());

        ServiceRequest r1, r2, r3;
        r1 = new ServiceRequest(c1.getClusterName(), null, "INSTALLED");
        r2 = new ServiceRequest(c2.getClusterName(), null, "INIT");
        r3 = new ServiceRequest(c2.getClusterName(), null, "INIT");

        Set<ServiceRequest> reqs = new HashSet<ServiceRequest>();
        reqs.addAll(Arrays.asList(r1, r2, r3));
        resp = ServiceResourceProviderTest.getServices(controller, reqs);
        Assert.assertEquals(3, resp.size());

    }

    @Test
    public void testGetServiceComponents() throws AmbariException {
        clusters.addCluster("c1");
        Cluster c1 = clusters.getCluster("c1");
        c1.setDesiredStackVersion(new StackId("HDP-0.2"));
        Service s1 = serviceFactory.createNew(c1, "HDFS");
        c1.addService(s1);
        s1.setDesiredState(State.INSTALLED);
        s1.persist();
        ServiceComponent sc1 = serviceComponentFactory.createNew(s1, "DATANODE");
        s1.addServiceComponent(sc1);
        sc1.persist();
        sc1.setDesiredStackVersion(new StackId("HDP-0.1"));
        sc1.setDesiredState(State.UNINSTALLED);

        ServiceComponentRequest r = new ServiceComponentRequest("c1", s1.getName(), sc1.getName(), null);

        Set<ServiceComponentResponse> resps = ComponentResourceProviderTest.getComponents(controller,
                Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        ServiceComponentResponse resp = resps.iterator().next();

        Assert.assertEquals(c1.getClusterName(), resp.getClusterName());
        Assert.assertEquals(sc1.getName(), resp.getComponentName());
        Assert.assertEquals(s1.getName(), resp.getServiceName());
        Assert.assertEquals("HDP-0.1", resp.getDesiredStackVersion());
        Assert.assertEquals(sc1.getDesiredState().toString(), resp.getDesiredState());
        Assert.assertEquals(c1.getClusterId(), resp.getClusterId().longValue());

    }

    @Test
    public void testGetServiceComponentsWithFilters() throws AmbariException {
        clusters.addCluster("c1");
        clusters.addCluster("c2");
        Cluster c1 = clusters.getCluster("c1");
        Cluster c2 = clusters.getCluster("c2");
        c1.setDesiredStackVersion(new StackId("HDP-0.2"));
        c2.setDesiredStackVersion(new StackId("HDP-0.2"));

        Service s1 = serviceFactory.createNew(c1, "HDFS");
        Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
        Service s3 = serviceFactory.createNew(c1, "HBASE");
        Service s4 = serviceFactory.createNew(c2, "HIVE");
        Service s5 = serviceFactory.createNew(c2, "ZOOKEEPER");

        c1.addService(s1);
        c1.addService(s2);
        c1.addService(s3);
        c2.addService(s4);
        c2.addService(s5);

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);
        s4.setDesiredState(State.INSTALLED);

        s1.persist();
        s2.persist();
        s3.persist();
        s4.persist();
        s5.persist();

        ServiceComponent sc1 = serviceComponentFactory.createNew(s1, "DATANODE");
        ServiceComponent sc2 = serviceComponentFactory.createNew(s1, "NAMENODE");
        ServiceComponent sc3 = serviceComponentFactory.createNew(s3, "HBASE_REGIONSERVER");
        ServiceComponent sc4 = serviceComponentFactory.createNew(s4, "HIVE_SERVER");
        ServiceComponent sc5 = serviceComponentFactory.createNew(s4, "HIVE_CLIENT");
        ServiceComponent sc6 = serviceComponentFactory.createNew(s4, "MYSQL_SERVER");
        ServiceComponent sc7 = serviceComponentFactory.createNew(s5, "ZOOKEEPER_SERVER");
        ServiceComponent sc8 = serviceComponentFactory.createNew(s5, "ZOOKEEPER_CLIENT");

        s1.addServiceComponent(sc1);
        s1.addServiceComponent(sc2);
        s3.addServiceComponent(sc3);
        s4.addServiceComponent(sc4);
        s4.addServiceComponent(sc5);
        s4.addServiceComponent(sc6);
        s5.addServiceComponent(sc7);
        s5.addServiceComponent(sc8);

        sc1.setDesiredState(State.UNINSTALLED);
        sc3.setDesiredState(State.UNINSTALLED);
        sc5.setDesiredState(State.UNINSTALLED);
        sc6.setDesiredState(State.UNINSTALLED);
        sc7.setDesiredState(State.UNINSTALLED);
        sc8.setDesiredState(State.UNINSTALLED);

        sc1.persist();
        sc2.persist();
        sc3.persist();
        sc4.persist();
        sc5.persist();
        sc6.persist();
        sc7.persist();
        sc8.persist();

        ServiceComponentRequest r = new ServiceComponentRequest(null, null, null, null);

        try {
            ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
            fail("Expected failure for invalid cluster");
        } catch (Exception e) {
            // Expected
        }

        // all comps per cluster
        r = new ServiceComponentRequest(c1.getClusterName(), null, null, null);
        Set<ServiceComponentResponse> resps = ComponentResourceProviderTest.getComponents(controller,
                Collections.singleton(r));
        Assert.assertEquals(3, resps.size());

        // all comps per cluster filter on state
        r = new ServiceComponentRequest(c2.getClusterName(), null, null, State.UNINSTALLED.toString());
        resps = ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
        Assert.assertEquals(4, resps.size());

        // all comps for given service
        r = new ServiceComponentRequest(c2.getClusterName(), s5.getName(), null, null);
        resps = ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        // all comps for given service filter by state
        r = new ServiceComponentRequest(c2.getClusterName(), s4.getName(), null, State.INIT.toString());
        resps = ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
        Assert.assertEquals(sc4.getName(), resps.iterator().next().getComponentName());

        // get single given comp
        r = new ServiceComponentRequest(c2.getClusterName(), null, sc5.getName(), State.INIT.toString());
        resps = ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
        Assert.assertEquals(sc5.getName(), resps.iterator().next().getComponentName());

        // get single given comp and given svc
        r = new ServiceComponentRequest(c2.getClusterName(), s4.getName(), sc5.getName(), State.INIT.toString());
        resps = ComponentResourceProviderTest.getComponents(controller, Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
        Assert.assertEquals(sc5.getName(), resps.iterator().next().getComponentName());

        ServiceComponentRequest r1, r2, r3;
        Set<ServiceComponentRequest> reqs = new HashSet<ServiceComponentRequest>();
        r1 = new ServiceComponentRequest(c2.getClusterName(), null, null, State.UNINSTALLED.toString());
        r2 = new ServiceComponentRequest(c1.getClusterName(), null, null, null);
        r3 = new ServiceComponentRequest(c1.getClusterName(), null, null, State.INIT.toString());
        reqs.addAll(Arrays.asList(r1, r2, r3));
        resps = ComponentResourceProviderTest.getComponents(controller, reqs);
        Assert.assertEquals(7, resps.size());
    }

    @Test
    public void testGetServiceComponentHosts() throws AmbariException {
        Cluster c1 = setupClusterWithHosts("c1", "HDP-0.1", new ArrayList<String>() {
            {
                add("h1");
            }
        }, "centos5");
        Service s1 = serviceFactory.createNew(c1, "HDFS");
        c1.addService(s1);
        s1.persist();
        ServiceComponent sc1 = serviceComponentFactory.createNew(s1, "DATANODE");
        s1.addServiceComponent(sc1);
        sc1.setDesiredState(State.UNINSTALLED);
        sc1.persist();
        ServiceComponentHost sch1 = serviceComponentHostFactory.createNew(sc1, "h1");
        sc1.addServiceComponentHost(sch1);
        sch1.setDesiredState(State.INSTALLED);
        sch1.setState(State.INSTALLING);
        sch1.setDesiredStackVersion(new StackId("HDP-1.1.0"));
        sch1.setStackVersion(new StackId("HDP-0.1"));

        sch1.persist();

        sch1.updateActualConfigs(new HashMap<String, Map<String, String>>() {
            {
                put("global", new HashMap<String, String>() {
                    {
                        put("tag", "version1");
                    }
                });
            }
        });

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(c1.getClusterName(), null, null, null,
                null);
        Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        ServiceComponentHostResponse resp = resps.iterator().next();

        Assert.assertEquals(c1.getClusterName(), resp.getClusterName());
        Assert.assertEquals(sc1.getName(), resp.getComponentName());
        Assert.assertEquals(s1.getName(), resp.getServiceName());
        Assert.assertEquals(sch1.getHostName(), resp.getHostname());
        Assert.assertEquals(sch1.getDesiredState().toString(), resp.getDesiredState());
        Assert.assertEquals(sch1.getState().toString(), resp.getLiveState());
        Assert.assertEquals(sch1.getStackVersion().getStackId(), resp.getStackVersion());
        Assert.assertNotNull(resp.getActualConfigs());
        Assert.assertEquals(1, resp.getActualConfigs().size());
    }

    @Test
    public void testGetServiceComponentHostsWithStaleConfigFilter() throws AmbariException {

        final String host1 = "h1";
        final String host2 = "h2";
        Long clusterId = 1L;
        String clusterName = "foo1";
        setupClusterWithHosts(clusterName, "HDP-2.0.5", new ArrayList<String>() {
            {
                add(host1);
                add(host2);
            }
        }, "centos5");
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1;
        cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);
        ClusterRequest crReq = new ClusterRequest(clusterId, clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Start
        startService(clusterName, serviceName, false, false);

        //Update actual config
        HashMap<String, Map<String, String>> actualConfig = new HashMap<String, Map<String, String>>() {
            {
                put("hdfs-site", new HashMap<String, String>() {
                    {
                        put("tag", "version1");
                    }
                });
            }
        };
        HashMap<String, Map<String, String>> actualConfigOld = new HashMap<String, Map<String, String>>() {
            {
                put("hdfs-site", new HashMap<String, String>() {
                    {
                        put("tag", "version0");
                    }
                });
            }
        };

        Service s1 = clusters.getCluster(clusterName).getService(serviceName);
        s1.getServiceComponent(componentName1).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
        s1.getServiceComponent(componentName3).getServiceComponentHost(host1).updateActualConfigs(actualConfigOld);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host2).updateActualConfigs(actualConfigOld);
        s1.getServiceComponent(componentName3).getServiceComponentHost(host2).updateActualConfigs(actualConfig);

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(5, resps.size());

        //Get all host components with stale config = true
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setStaleConfig("true");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        //Get all host components with stale config = false
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setStaleConfig("false");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(3, resps.size());

        //Get all host components with stale config = false and hostname filter
        r = new ServiceComponentHostRequest(clusterName, null, null, host1, null);
        r.setStaleConfig("false");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        //Get all host components with stale config = false and hostname filter
        r = new ServiceComponentHostRequest(clusterName, null, null, host2, null);
        r.setStaleConfig("true");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
    }

    @Test
    public void testServiceComponentHostsWithDecommissioned() throws Exception {

        final String host1 = "h1";
        final String host2 = "h2";
        String clusterName = "foo1";
        setupClusterWithHosts(clusterName, "HDP-2.0.7", new ArrayList<String>() {
            {
                add(host1);
                add(host2);
            }
        }, "centos5");
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Start
        startService(clusterName, serviceName, false, false);

        Service s1 = clusters.getCluster(clusterName).getService(serviceName);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host1)
                .setComponentAdminState(HostComponentAdminState.DECOMMISSIONED);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host2)
                .setComponentAdminState(HostComponentAdminState.INSERVICE);

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(5, resps.size());

        //Get all host components with decommissiond = true
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setAdminState("DECOMMISSIONED");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        //Get all host components with decommissioned = false
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setAdminState("INSERVICE");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        //Get all host components with decommissioned = some random string
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setAdminState("INSTALLED");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(0, resps.size());

        //Update adminState
        r = new ServiceComponentHostRequest(clusterName, "HDFS", "DATANODE", host2, null);
        r.setAdminState("DECOMMISSIONED");
        try {
            updateHostComponents(Collections.singleton(r), new HashMap<String, String>(), false);
            Assert.fail("Must throw exception when decommission attribute is updated.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getMessage().contains("Property adminState cannot be modified through update"));
        }
    }

    @Test
    public void testHbaseDecommission() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-2.0.7"));
        String serviceName = "HBASE";
        createService(clusterName, serviceName, null);
        String componentName1 = "HBASE_MASTER";
        String componentName2 = "HBASE_REGIONSERVER";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName1, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);

        RequestOperationLevel level = new RequestOperationLevel(Resource.Type.HostComponent, clusterName, null,
                null, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Start
        startService(clusterName, serviceName, false, false);

        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        Assert.assertEquals(State.STARTED, s.getDesiredState());
        ServiceComponentHost scHost = s.getServiceComponent("HBASE_REGIONSERVER").getServiceComponentHost("h2");
        Assert.assertEquals(HostComponentAdminState.INSERVICE, scHost.getComponentAdminState());

        // Decommission one RS
        Map<String, String> params = new HashMap<String, String>() {
            {
                put("excluded_hosts", "h2");
                put("align_maintenance_state", "true");
            }
        };
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HBASE", "HBASE_MASTER", null);
        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        resourceFilters.add(resourceFilter);

        ExecuteActionRequest request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, resourceFilters,
                level, params, false);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        RequestStatusResponse response = controller.createAction(request, requestProperties);

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        ExecutionCommand execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(1, storedTasks.size());
        Assert.assertEquals(HostComponentAdminState.DECOMMISSIONED, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.ON, scHost.getMaintenanceState());
        HostRoleCommand command = storedTasks.get(0);
        Assert.assertTrue("DECOMMISSION, Excluded: h2".equals(command.getCommandDetail()));
        Assert.assertTrue("DECOMMISSION".equals(command.getCustomCommandName()));
        Map<String, String> cmdParams = command.getExecutionCommandWrapper().getExecutionCommand()
                .getCommandParams();
        Assert.assertTrue(cmdParams.containsKey("mark_draining_only"));
        Assert.assertEquals("false", cmdParams.get("mark_draining_only"));
        Assert.assertEquals(Role.HBASE_MASTER, command.getRole());
        Assert.assertEquals(RoleCommand.CUSTOM_COMMAND, command.getRoleCommand());
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));

        // RS stops
        s.getServiceComponent("HBASE_REGIONSERVER").getServiceComponentHost("h2").setState(State.INSTALLED);

        // Remove RS from draining
        params = new HashMap<String, String>() {
            {
                put("excluded_hosts", "h2");
                put("mark_draining_only", "true");
                put("slave_type", "HBASE_REGIONSERVER");
                put("align_maintenance_state", "true");
            }
        };
        resourceFilter = new RequestResourceFilter("HBASE", "HBASE_MASTER", null);
        ArrayList<RequestResourceFilter> filters = new ArrayList<RequestResourceFilter>();
        filters.add(resourceFilter);
        request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, filters, level, params, false);

        response = controller.createAction(request, requestProperties);

        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(1, storedTasks.size());
        Assert.assertEquals(HostComponentAdminState.DECOMMISSIONED, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.ON, scHost.getMaintenanceState());
        command = storedTasks.get(0);
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));
        Assert.assertTrue("DECOMMISSION, Excluded: h2".equals(command.getCommandDetail()));
        Assert.assertTrue("DECOMMISSION".equals(command.getCustomCommandName()));
        cmdParams = command.getExecutionCommandWrapper().getExecutionCommand().getCommandParams();
        Assert.assertTrue(cmdParams.containsKey("mark_draining_only"));
        Assert.assertEquals("true", cmdParams.get("mark_draining_only"));

        //Recommission
        params = new HashMap<String, String>() {
            {
                put("included_hosts", "h2");
            }
        };
        request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, resourceFilters, level, params,
                false);

        response = controller.createAction(request, requestProperties);

        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(1, storedTasks.size());
        Assert.assertEquals(HostComponentAdminState.INSERVICE, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.ON, scHost.getMaintenanceState());
        command = storedTasks.get(0);
        Assert.assertTrue("DECOMMISSION, Included: h2".equals(command.getCommandDetail()));
        Assert.assertTrue("DECOMMISSION".equals(command.getCustomCommandName()));
        cmdParams = command.getExecutionCommandWrapper().getExecutionCommand().getCommandParams();
        Assert.assertTrue(cmdParams.containsKey("mark_draining_only"));
        Assert.assertEquals("false", cmdParams.get("mark_draining_only"));

        Assert.assertTrue(cmdParams.containsKey("excluded_hosts"));
        Assert.assertEquals("", cmdParams.get("excluded_hosts"));
        Assert.assertEquals(Role.HBASE_MASTER, command.getRole());
        Assert.assertEquals(RoleCommand.CUSTOM_COMMAND, command.getRoleCommand());
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));
    }

    private Cluster setupClusterWithHosts(String clusterName, String stackId, List<String> hosts, String osType)
            throws AmbariException {
        ClusterRequest r = new ClusterRequest(null, clusterName, stackId, null);
        controller.createCluster(r);
        Cluster c1 = clusters.getCluster(clusterName);
        for (String host : hosts) {
            addHost(host, clusterName);
        }
        return c1;
    }

    @Test
    public void testGetServiceComponentHostsWithFilters() throws AmbariException {
        Cluster c1 = setupClusterWithHosts("c1", "HDP-0.2", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
                add("h3");
            }
        }, "centos5");

        Service s1 = serviceFactory.createNew(c1, "HDFS");
        Service s2 = serviceFactory.createNew(c1, "MAPREDUCE");
        Service s3 = serviceFactory.createNew(c1, "HBASE");

        c1.addService(s1);
        c1.addService(s2);
        c1.addService(s3);

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);

        s1.persist();
        s2.persist();
        s3.persist();

        ServiceComponent sc1 = serviceComponentFactory.createNew(s1, "DATANODE");
        ServiceComponent sc2 = serviceComponentFactory.createNew(s1, "NAMENODE");
        ServiceComponent sc3 = serviceComponentFactory.createNew(s3, "HBASE_REGIONSERVER");

        s1.addServiceComponent(sc1);
        s1.addServiceComponent(sc2);
        s3.addServiceComponent(sc3);

        sc1.setDesiredState(State.UNINSTALLED);
        sc3.setDesiredState(State.UNINSTALLED);

        sc1.persist();
        sc2.persist();
        sc3.persist();

        ServiceComponentHost sch1 = serviceComponentHostFactory.createNew(sc1, "h1");
        ServiceComponentHost sch2 = serviceComponentHostFactory.createNew(sc1, "h2");
        ServiceComponentHost sch3 = serviceComponentHostFactory.createNew(sc1, "h3");
        ServiceComponentHost sch4 = serviceComponentHostFactory.createNew(sc2, "h1");
        ServiceComponentHost sch5 = serviceComponentHostFactory.createNew(sc2, "h2");
        ServiceComponentHost sch6 = serviceComponentHostFactory.createNew(sc3, "h3");

        sc1.addServiceComponentHost(sch1);
        sc1.addServiceComponentHost(sch2);
        sc1.addServiceComponentHost(sch3);
        sc2.addServiceComponentHost(sch4);
        sc2.addServiceComponentHost(sch5);
        sc3.addServiceComponentHost(sch6);

        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INIT);
        sch4.setDesiredState(State.INSTALLED);
        sch5.setDesiredState(State.UNINSTALLED);

        sch1.persist();
        sch2.persist();
        sch3.persist();
        sch4.persist();
        sch5.persist();
        sch6.persist();

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(null, null, null, null, null);

        try {
            controller.getHostComponents(Collections.singleton(r));
            fail("Expected failure for invalid cluster");
        } catch (Exception e) {
            // Expected
        }

        // all across cluster
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, null, null, null);
        Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(6, resps.size());

        // all for service
        r = new ServiceComponentHostRequest(c1.getClusterName(), s1.getName(), null, null, null);
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(5, resps.size());

        // all for component
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, sc3.getName(), null, null);
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        // all for host
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, null, "h2", null);
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        // all across cluster with state filter
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, null, null, State.UNINSTALLED.toString());
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        // all for service with state filter
        r = new ServiceComponentHostRequest(c1.getClusterName(), s1.getName(), null, null, State.INIT.toString());
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        // all for component with state filter
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, sc3.getName(), null,
                State.INSTALLED.toString());
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(0, resps.size());

        // all for host with state filter
        r = new ServiceComponentHostRequest(c1.getClusterName(), null, null, "h2", State.INIT.toString());
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        // for service and host
        r = new ServiceComponentHostRequest(c1.getClusterName(), s3.getName(), null, "h1", null);
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(0, resps.size());

        // single sch - given service and host and component
        r = new ServiceComponentHostRequest(c1.getClusterName(), s3.getName(), sc3.getName(), "h3",
                State.INSTALLED.toString());
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(0, resps.size());

        // single sch - given service and host and component
        r = new ServiceComponentHostRequest(c1.getClusterName(), s3.getName(), sc3.getName(), "h3", null);
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());

        ServiceComponentHostRequest r1, r2, r3;
        r1 = new ServiceComponentHostRequest(c1.getClusterName(), null, null, "h3", null);
        r2 = new ServiceComponentHostRequest(c1.getClusterName(), s3.getName(), sc3.getName(), "h2", null);
        r3 = new ServiceComponentHostRequest(c1.getClusterName(), null, null, "h2", null);
        Set<ServiceComponentHostRequest> reqs = new HashSet<ServiceComponentHostRequest>();
        reqs.addAll(Arrays.asList(r1, r2, r3));
        resps = controller.getHostComponents(reqs);
        Assert.assertEquals(4, resps.size());
    }

    @Test
    public void testGetHosts() throws AmbariException {
        setupClusterWithHosts("c1", "HDP-0.2", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
            }
        }, "centos5");

        setupClusterWithHosts("c2", "HDP-0.2", new ArrayList<String>() {
            {
                add("h3");
            }
        }, "centos5");
        clusters.addHost("h4");
        setOsFamily(clusters.getHost("h4"), "redhat", "5.9");
        clusters.getHost("h4").persist();

        Map<String, String> attrs = new HashMap<String, String>();
        attrs.put("a1", "b1");
        clusters.getHost("h3").setHostAttributes(attrs);
        attrs.put("a2", "b2");
        clusters.getHost("h4").setHostAttributes(attrs);

        HostRequest r = new HostRequest(null, null, null);

        Set<HostResponse> resps = HostResourceProviderTest.getHosts(controller, Collections.singleton(r));

        Assert.assertEquals(4, resps.size());

        Set<String> foundHosts = new HashSet<String>();

        for (HostResponse resp : resps) {
            foundHosts.add(resp.getHostname());
            if (resp.getHostname().equals("h1")) {
                Assert.assertEquals("c1", resp.getClusterName());
                Assert.assertEquals(2, resp.getHostAttributes().size());
            } else if (resp.getHostname().equals("h2")) {
                Assert.assertEquals("c1", resp.getClusterName());
                Assert.assertEquals(2, resp.getHostAttributes().size());
            } else if (resp.getHostname().equals("h3")) {
                Assert.assertEquals("c2", resp.getClusterName());
                Assert.assertEquals(3, resp.getHostAttributes().size());
            } else if (resp.getHostname().equals("h4")) {
                //todo: why wouldn't this be null?
                Assert.assertEquals("", resp.getClusterName());
                Assert.assertEquals(4, resp.getHostAttributes().size());
            } else {
                fail("Found invalid host");
            }
        }

        Assert.assertEquals(4, foundHosts.size());

        r = new HostRequest("h1", null, null);
        resps = HostResourceProviderTest.getHosts(controller, Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
        HostResponse resp = resps.iterator().next();
        Assert.assertEquals("h1", resp.getHostname());
        Assert.assertEquals("c1", resp.getClusterName());
        Assert.assertEquals(2, resp.getHostAttributes().size());

    }

    @Test
    public void testServiceUpdateBasic() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName = "HDFS";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        clusters.getCluster("foo1").setDesiredStackVersion(new StackId("HDP-0.2"));
        createService(clusterName, serviceName, State.INIT);

        Service s = clusters.getCluster(clusterName).getService(serviceName);
        Assert.assertNotNull(s);
        Assert.assertEquals(serviceName, s.getName());
        Assert.assertEquals(State.INIT, s.getDesiredState());
        Assert.assertEquals(clusterName, s.getCluster().getClusterName());

        Set<ServiceRequest> reqs = new HashSet<ServiceRequest>();
        ServiceRequest r;

        try {
            r = new ServiceRequest(clusterName, serviceName, State.INSTALLING.toString());
            reqs.clear();
            reqs.add(r);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected fail for invalid state transition");
        } catch (Exception e) {
            // Expected
        }

        r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        reqs.clear();
        reqs.add(r);
        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, reqs,
                mapRequestProps, true, false);
        Assert.assertNull(trackAction);
    }

    @Test
    public void testServiceUpdateInvalidRequest() throws AmbariException {
        // multiple clusters
        // dup services
        // multiple diff end states

        String clusterName1 = "foo1";
        createCluster(clusterName1);
        String clusterName2 = "foo2";
        createCluster(clusterName2);
        String serviceName1 = "HDFS";
        createService(clusterName1, serviceName1, null);
        String serviceName2 = "HBASE";
        String serviceName3 = "HBASE";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        try {
            createService(clusterName2, serviceName3, null);
            fail("Expected fail for invalid service for stack 0.1");
        } catch (Exception e) {
            // Expected
        }

        clusters.getCluster(clusterName1).setDesiredStackVersion(new StackId("HDP-0.2"));
        clusters.getCluster(clusterName2).setDesiredStackVersion(new StackId("HDP-0.2"));
        createService(clusterName1, serviceName2, null);
        createService(clusterName2, serviceName3, null);

        Set<ServiceRequest> reqs = new HashSet<ServiceRequest>();
        ServiceRequest req1, req2;
        try {
            reqs.clear();
            req1 = new ServiceRequest(clusterName1, serviceName1, State.INSTALLED.toString());
            req2 = new ServiceRequest(clusterName2, serviceName2, State.INSTALLED.toString());
            reqs.add(req1);
            reqs.add(req2);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected failure for multi cluster update");
        } catch (Exception e) {
            // Expected
        }

        try {
            reqs.clear();
            req1 = new ServiceRequest(clusterName1, serviceName1, State.INSTALLED.toString());
            req2 = new ServiceRequest(clusterName1, serviceName1, State.INSTALLED.toString());
            reqs.add(req1);
            reqs.add(req2);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected failure for dups services");
        } catch (Exception e) {
            // Expected
        }

        clusters.getCluster(clusterName1).getService(serviceName2).setDesiredState(State.INSTALLED);

        try {
            reqs.clear();
            req1 = new ServiceRequest(clusterName1, serviceName1, State.INSTALLED.toString());
            req2 = new ServiceRequest(clusterName1, serviceName2, State.STARTED.toString());
            reqs.add(req1);
            reqs.add(req2);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected failure for different states");
        } catch (Exception e) {
            // Expected
        }

    }

    @Test
    public void testServiceUpdateInvalidUpdates() {
        // FIXME test all invalid transitions
    }

    @Test
    public void testServiceUpdateRecursive() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.2"));
        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);
        String serviceName2 = "HBASE";
        createService(clusterName, serviceName2, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HBASE_MASTER";
        String componentName4 = "HDFS_CLIENT";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName3, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName4, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r4 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r5 = new ServiceComponentHostRequest(clusterName, serviceName2, componentName3,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r6 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName4,
                host2, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        set1.add(r4);
        set1.add(r5);
        set1.add(r6);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);
        Service s2 = c1.getService(serviceName2);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        ServiceComponent sc3 = s2.getServiceComponent(componentName3);
        ServiceComponent sc4 = s1.getServiceComponent(componentName4);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc2.getServiceComponentHost(host1);
        ServiceComponentHost sch3 = sc1.getServiceComponentHost(host2);
        ServiceComponentHost sch4 = sc2.getServiceComponentHost(host2);
        ServiceComponentHost sch5 = sc3.getServiceComponentHost(host1);
        ServiceComponentHost sch6 = sc4.getServiceComponentHost(host2);

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INIT);
        sc3.setDesiredState(State.STARTED);
        sc4.setDesiredState(State.INSTALLED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);
        sch3.setDesiredState(State.INSTALLED);
        sch4.setDesiredState(State.INSTALLED);
        sch5.setDesiredState(State.INSTALLED);
        sch6.setDesiredState(State.INSTALLED);
        sch1.setState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INSTALLED);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);
        sch6.setState(State.INSTALLED);

        Set<ServiceRequest> reqs = new HashSet<ServiceRequest>();
        ServiceRequest req1, req2;
        try {
            reqs.clear();
            req1 = new ServiceRequest(clusterName, serviceName1, State.STARTED.toString());
            reqs.add(req1);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected failure for invalid state update");
        } catch (Exception e) {
            // Expected
        }

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INSTALLED);
        sc3.setDesiredState(State.STARTED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);
        sch3.setDesiredState(State.INSTALLED);
        sch4.setDesiredState(State.INSTALLED);
        sch5.setDesiredState(State.INSTALLED);
        sch1.setState(State.INIT);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INIT);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);

        try {
            reqs.clear();
            req1 = new ServiceRequest(clusterName, serviceName1, State.STARTED.toString());
            reqs.add(req1);
            ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
            fail("Expected failure for invalid state update");
        } catch (Exception e) {
            // Expected
        }

        s1.setDesiredState(State.INSTALLED);
        s2.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INSTALLED);
        sc3.setDesiredState(State.STARTED);
        sch1.setDesiredState(State.STARTED);
        sch2.setDesiredState(State.STARTED);
        sch3.setDesiredState(State.STARTED);
        sch4.setDesiredState(State.STARTED);
        sch5.setDesiredState(State.STARTED);
        sch1.setState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INSTALLED);
        sch4.setState(State.STARTED);
        sch5.setState(State.INSTALLED);

        reqs.clear();
        req1 = new ServiceRequest(clusterName, serviceName1, State.STARTED.toString());
        req2 = new ServiceRequest(clusterName, serviceName2, State.STARTED.toString());
        reqs.add(req1);
        reqs.add(req2);
        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, reqs,
                mapRequestProps, true, false);

        Assert.assertEquals(State.STARTED, s1.getDesiredState());
        Assert.assertEquals(State.STARTED, s2.getDesiredState());
        Assert.assertEquals(State.STARTED, sc1.getDesiredState());
        Assert.assertEquals(State.STARTED, sc2.getDesiredState());
        Assert.assertEquals(State.STARTED, sc3.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sc4.getDesiredState());
        Assert.assertEquals(State.STARTED, sch1.getDesiredState());
        Assert.assertEquals(State.STARTED, sch2.getDesiredState());
        Assert.assertEquals(State.STARTED, sch3.getDesiredState());
        Assert.assertEquals(State.STARTED, sch4.getDesiredState());
        Assert.assertEquals(State.STARTED, sch5.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch6.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch1.getState());
        Assert.assertEquals(State.INSTALLED, sch2.getState());
        Assert.assertEquals(State.INSTALLED, sch3.getState());
        Assert.assertEquals(State.STARTED, sch4.getState());
        Assert.assertEquals(State.INSTALLED, sch5.getState());
        Assert.assertEquals(State.INSTALLED, sch6.getState());

        long requestId = trackAction.getRequestId();
        List<Stage> stages = actionDB.getAllStages(requestId);

        for (Stage stage : stages) {
            LOG.debug("Stage dump: " + stage.toString());
        }

        Assert.assertTrue(!stages.isEmpty());
        Assert.assertEquals(3, stages.size());

        // expected
        // sch1 to start
        // sch2 to start
        // sch3 to start
        // sch5 to start
        Stage stage1 = null, stage2 = null, stage3 = null;
        for (Stage s : stages) {
            if (s.getStageId() == 0) {
                stage1 = s;
            }
            if (s.getStageId() == 1) {
                stage2 = s;
            }
            if (s.getStageId() == 2) {
                stage3 = s;
            }
        }

        Assert.assertEquals(2, stage1.getExecutionCommands(host1).size());
        Assert.assertEquals(1, stage1.getExecutionCommands(host2).size());
        Assert.assertEquals(1, stage2.getExecutionCommands(host1).size());

        Assert.assertNotNull(stage1.getExecutionCommandWrapper(host1, "NAMENODE"));
        Assert.assertNotNull(stage1.getExecutionCommandWrapper(host1, "DATANODE"));
        Assert.assertNotNull(stage1.getExecutionCommandWrapper(host2, "NAMENODE"));
        Assert.assertNotNull(stage2.getExecutionCommandWrapper(host1, "HBASE_MASTER"));
        Assert.assertNull(stage1.getExecutionCommandWrapper(host2, "DATANODE"));
        Assert.assertNotNull(stage3.getExecutionCommandWrapper(host1, "HBASE_SERVICE_CHECK"));
        Assert.assertNotNull(stage2.getExecutionCommandWrapper(host2, "HDFS_SERVICE_CHECK"));

        Type type = new TypeToken<Map<String, String>>() {
        }.getType();

        for (Stage s : stages) {
            for (List<ExecutionCommandWrapper> list : s.getExecutionCommands().values()) {
                for (ExecutionCommandWrapper ecw : list) {
                    if (ecw.getExecutionCommand().getRole().contains("SERVICE_CHECK")) {
                        Map<String, String> hostParams = StageUtils.getGson().fromJson(s.getHostParamsStage(),
                                type);
                        Assert.assertNotNull(hostParams);
                        Assert.assertTrue(hostParams.size() > 0);
                        Assert.assertTrue(hostParams.containsKey("stack_version"));
                        Assert.assertEquals(hostParams.get("stack_version"),
                                c1.getDesiredStackVersion().getStackVersion());
                    }
                }
            }
        }

        // manually set live state
        sch1.setState(State.STARTED);
        sch2.setState(State.STARTED);
        sch3.setState(State.STARTED);
        sch4.setState(State.STARTED);
        sch5.setState(State.STARTED);

        // test no-op
        reqs.clear();
        req1 = new ServiceRequest(clusterName, serviceName1, State.STARTED.toString());
        req2 = new ServiceRequest(clusterName, serviceName2, State.STARTED.toString());
        reqs.add(req1);
        reqs.add(req2);
        trackAction = ServiceResourceProviderTest.updateServices(controller, reqs, mapRequestProps, true, false);
        Assert.assertNull(trackAction);

    }

    @Test
    public void testServiceComponentUpdateRecursive() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName3, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r4 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r5 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName3,
                host1, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        set1.add(r4);
        set1.add(r5);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        ServiceComponent sc3 = s1.getServiceComponent(componentName3);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc2.getServiceComponentHost(host1);
        ServiceComponentHost sch3 = sc1.getServiceComponentHost(host2);
        ServiceComponentHost sch4 = sc2.getServiceComponentHost(host2);
        ServiceComponentHost sch5 = sc3.getServiceComponentHost(host1);

        s1.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.INIT);
        sc2.setDesiredState(State.INIT);
        sc3.setDesiredState(State.STARTED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);
        sch3.setDesiredState(State.STARTED);
        sch4.setDesiredState(State.INSTALLED);
        sch5.setDesiredState(State.INSTALLED);
        sch1.setState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.STARTED);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.UNKNOWN);

        Set<ServiceComponentRequest> reqs = new HashSet<ServiceComponentRequest>();
        ServiceComponentRequest req1, req2, req3;

        // confirm an UNKOWN doesn't fail
        req1 = new ServiceComponentRequest(clusterName, serviceName1, sc3.getName(), State.INSTALLED.toString());
        reqs.add(req1);
        ComponentResourceProviderTest.updateComponents(controller, reqs, Collections.<String, String>emptyMap(),
                true);
        try {
            reqs.clear();
            req1 = new ServiceComponentRequest(clusterName, serviceName1, sc1.getName(), State.INIT.toString());
            reqs.add(req1);
            ComponentResourceProviderTest.updateComponents(controller, reqs, Collections.<String, String>emptyMap(),
                    true);
            fail("Expected failure for invalid state update");
        } catch (Exception e) {
            // Expected
        }

        s1.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INSTALLED);
        sc3.setDesiredState(State.STARTED);
        sch1.setDesiredState(State.INIT);
        sch2.setDesiredState(State.INIT);
        sch3.setDesiredState(State.INIT);
        sch4.setDesiredState(State.INIT);
        sch5.setDesiredState(State.INIT);
        sch1.setState(State.INIT);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INIT);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);

        try {
            reqs.clear();
            req1 = new ServiceComponentRequest(clusterName, serviceName1, sc1.getName(), State.STARTED.toString());
            reqs.add(req1);
            ComponentResourceProviderTest.updateComponents(controller, reqs, Collections.<String, String>emptyMap(),
                    true);
            fail("Expected failure for invalid state update");
        } catch (Exception e) {
            // Expected
        }

        s1.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INIT);
        sc3.setDesiredState(State.STARTED);
        sch1.setDesiredState(State.INIT);
        sch2.setDesiredState(State.INIT);
        sch3.setDesiredState(State.INIT);
        sch4.setDesiredState(State.INIT);
        sch5.setDesiredState(State.INIT);
        sch1.setState(State.STARTED);
        sch2.setState(State.INIT);
        sch3.setState(State.INSTALLED);
        sch4.setState(State.STARTED);
        sch5.setState(State.INIT);

        reqs.clear();
        req1 = new ServiceComponentRequest(clusterName, serviceName1, sc1.getName(), State.INSTALLED.toString());
        req2 = new ServiceComponentRequest(clusterName, serviceName1, sc2.getName(), State.INSTALLED.toString());
        req3 = new ServiceComponentRequest(clusterName, serviceName1, sc3.getName(), State.INSTALLED.toString());
        reqs.add(req1);
        reqs.add(req2);
        reqs.add(req3);
        RequestStatusResponse trackAction = ComponentResourceProviderTest.updateComponents(controller, reqs,
                Collections.<String, String>emptyMap(), true);

        Assert.assertEquals(State.INSTALLED, s1.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sc1.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sc2.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sc3.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch1.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch2.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch3.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch4.getDesiredState());
        Assert.assertEquals(State.INSTALLED, sch5.getDesiredState());
        Assert.assertEquals(State.STARTED, sch1.getState());
        Assert.assertEquals(State.INIT, sch2.getState());
        Assert.assertEquals(State.INSTALLED, sch3.getState());
        Assert.assertEquals(State.STARTED, sch4.getState());
        Assert.assertEquals(State.INIT, sch5.getState());

        long requestId = trackAction.getRequestId();
        List<Stage> stages = actionDB.getAllStages(requestId);
        Assert.assertTrue(!stages.isEmpty());

        // FIXME check stage count

        for (Stage stage : stages) {
            LOG.debug("Stage dump: " + stage.toString());
        }

        // FIXME verify stages content - execution commands, etc

        // maually set live state
        sch1.setState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INSTALLED);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);

        // test no-op
        reqs.clear();
        req1 = new ServiceComponentRequest(clusterName, serviceName1, sc1.getName(), State.INSTALLED.toString());
        req2 = new ServiceComponentRequest(clusterName, serviceName1, sc2.getName(), State.INSTALLED.toString());
        reqs.add(req1);
        reqs.add(req2);
        trackAction = ComponentResourceProviderTest.updateComponents(controller, reqs,
                Collections.<String, String>emptyMap(), true);
        Assert.assertNull(trackAction);
    }

    @Test
    public void testServiceComponentHostUpdateRecursive() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName3, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r4 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r5 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName3,
                host1, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        set1.add(r4);
        set1.add(r5);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        ServiceComponent sc3 = s1.getServiceComponent(componentName3);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc2.getServiceComponentHost(host1);
        ServiceComponentHost sch3 = sc1.getServiceComponentHost(host2);
        ServiceComponentHost sch4 = sc2.getServiceComponentHost(host2);
        ServiceComponentHost sch5 = sc3.getServiceComponentHost(host1);

        s1.setDesiredState(State.INIT);
        sc1.setDesiredState(State.INIT);
        sc2.setDesiredState(State.INIT);
        sc3.setDesiredState(State.INIT);
        sch1.setDesiredState(State.INIT);
        sch2.setDesiredState(State.INIT);
        sch3.setDesiredState(State.INIT);
        sch4.setDesiredState(State.INSTALLED);
        sch5.setDesiredState(State.INSTALLED);
        sch1.setState(State.INIT);
        sch2.setState(State.INSTALL_FAILED);
        sch3.setState(State.INIT);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);

        ServiceComponentHostRequest req1, req2, req3, req4, req5;
        Set<ServiceComponentHostRequest> reqs = new HashSet<ServiceComponentHostRequest>();

        try {
            reqs.clear();
            req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                    State.STARTED.toString());
            reqs.add(req1);
            updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
            fail("Expected failure for invalid transition");
        } catch (Exception e) {
            // Expected
        }

        try {
            reqs.clear();
            req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                    State.INSTALLED.toString());
            req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                    State.INSTALLED.toString());
            req3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2, host1,
                    State.INSTALLED.toString());
            req4 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2, host2,
                    State.INSTALLED.toString());
            req5 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName3, host1,
                    State.STARTED.toString());
            reqs.add(req1);
            reqs.add(req2);
            reqs.add(req3);
            reqs.add(req4);
            reqs.add(req5);
            updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
            fail("Expected failure for invalid states");
        } catch (Exception e) {
            // Expected
        }

        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, null, componentName1, host1,
                State.INSTALLED.toString());
        req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                State.INSTALLED.toString());
        req3 = new ServiceComponentHostRequest(clusterName, null, componentName2, host1,
                State.INSTALLED.toString());
        req4 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2, host2,
                State.INSTALLED.toString());
        req5 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName3, host1,
                State.INSTALLED.toString());
        reqs.add(req1);
        reqs.add(req2);
        reqs.add(req3);
        reqs.add(req4);
        reqs.add(req5);
        RequestStatusResponse trackAction = updateHostComponents(reqs, Collections.<String, String>emptyMap(),
                true);
        Assert.assertNotNull(trackAction);

        long requestId = trackAction.getRequestId();

        Assert.assertFalse(actionDB.getAllStages(requestId).isEmpty());
        List<Stage> stages = actionDB.getAllStages(requestId);
        // FIXME check stage count

        for (Stage stage : stages) {
            LOG.debug("Stage dump: " + stage.toString());
        }

        // FIXME verify stages content - execution commands, etc

        // manually set live state
        sch1.setState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch3.setState(State.INSTALLED);
        sch4.setState(State.INSTALLED);
        sch5.setState(State.INSTALLED);

        // test no-op
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.INSTALLED.toString());
        req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                State.INSTALLED.toString());
        reqs.add(req1);
        reqs.add(req2);
        trackAction = updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
        Assert.assertNull(trackAction);
    }

    @Ignore
    @Test
    public void testServiceComponentHostUpdateStackId() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host2, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host1, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc1.getServiceComponentHost(host2);
        ServiceComponentHost sch3 = sc2.getServiceComponentHost(host1);

        s1.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.INSTALLED);
        sc2.setDesiredState(State.INSTALLED);

        ServiceComponentHostRequest req1;
        ServiceComponentHostRequest req2;
        ServiceComponentHostRequest req3;
        Set<ServiceComponentHostRequest> reqs = new HashSet<ServiceComponentHostRequest>();

        StackId newStack = new StackId("HDP-0.2");
        StackId oldStack = new StackId("HDP-0.1");
        c1.setCurrentStackVersion(newStack);
        c1.setDesiredStackVersion(newStack);
        sch1.setState(State.INSTALLED);
        sch2.setState(State.UPGRADING);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);

        sch1.setStackVersion(oldStack);
        sch2.setStackVersion(oldStack);
        sch1.setDesiredStackVersion(newStack);
        sch2.setDesiredStackVersion(oldStack);

        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.INSTALLED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                State.INSTALLED.toString());
        req2.setDesiredStackId("HDP-0.2");
        reqs.add(req2);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "testServiceComponentHostUpdateStackId");

        RequestStatusResponse resp = updateHostComponents(reqs, mapRequestProps, true);
        List<Stage> stages = actionDB.getAllStages(resp.getRequestId());
        Assert.assertEquals(1, stages.size());
        Assert.assertEquals(2, stages.get(0).getOrderedHostRoleCommands().size());
        Assert.assertEquals("testServiceComponentHostUpdateStackId", stages.get(0).getRequestContext());
        Assert.assertEquals(State.UPGRADING, sch1.getState());
        Assert.assertEquals(State.UPGRADING, sch2.getState());
        sch1.refresh();
        Assert.assertTrue(sch1.getDesiredStackVersion().compareTo(newStack) == 0);
        sch2.refresh();
        Assert.assertTrue(sch2.getDesiredStackVersion().compareTo(newStack) == 0);
        for (HostRoleCommand command : stages.get(0).getOrderedHostRoleCommands()) {
            ExecutionCommand execCommand = command.getExecutionCommandWrapper().getExecutionCommand();
            Assert.assertTrue(execCommand.getCommandParams().containsKey("source_stack_version"));
            Assert.assertTrue(execCommand.getCommandParams().containsKey("target_stack_version"));
            Assert.assertEquals(RoleCommand.UPGRADE, execCommand.getRoleCommand());
        }

        sch1.setState(State.INSTALLED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setState(State.UPGRADING);
        sch2.setDesiredState(State.INSTALLED);
        sch3.setState(State.UPGRADING);
        sch3.setDesiredState(State.INSTALLED);

        sch3.setStackVersion(oldStack);
        sch3.setDesiredStackVersion(newStack);

        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.INSTALLED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                State.INSTALLED.toString());
        req2.setDesiredStackId("HDP-0.2");
        reqs.add(req2);
        req3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2, host1,
                State.INSTALLED.toString());
        req3.setDesiredStackId("HDP-0.2");
        reqs.add(req3);

        resp = updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
        stages = actionDB.getAllStages(resp.getRequestId());
        Assert.assertEquals(2, stages.size());
        Assert.assertEquals(2, stages.get(0).getOrderedHostRoleCommands().size());
        Assert.assertEquals("", stages.get(0).getRequestContext());
        Assert.assertEquals(State.UPGRADING, sch1.getState());
        Assert.assertEquals(State.UPGRADING, sch2.getState());
        Assert.assertEquals(State.UPGRADING, sch3.getState());
        sch1.refresh();
        Assert.assertTrue(sch1.getDesiredStackVersion().compareTo(newStack) == 0);
        sch2.refresh();
        Assert.assertTrue(sch2.getDesiredStackVersion().compareTo(newStack) == 0);
        sch3.refresh();
        Assert.assertTrue(sch3.getDesiredStackVersion().compareTo(newStack) == 0);
        for (Stage stage : stages) {
            for (HostRoleCommand command : stage.getOrderedHostRoleCommands()) {
                ExecutionCommand execCommand = command.getExecutionCommandWrapper().getExecutionCommand();
                Assert.assertTrue(execCommand.getCommandParams().containsKey("source_stack_version"));
                Assert.assertTrue(execCommand.getCommandParams().containsKey("target_stack_version"));
                Assert.assertEquals("{\"stackName\":\"HDP\",\"stackVersion\":\"0.2\"}",
                        execCommand.getCommandParams().get("target_stack_version"));
                Assert.assertEquals(RoleCommand.UPGRADE, execCommand.getRoleCommand());
            }
        }
    }

    @Ignore
    @Test
    public void testServiceComponentHostUpdateStackIdError() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);
        String componentName1 = "NAMENODE";
        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        String host1 = "h1";
        String host2 = "h2";
        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host2, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc1.getServiceComponentHost(host2);

        s1.setDesiredState(State.INIT);
        sc1.setDesiredState(State.INIT);

        ServiceComponentHostRequest req1;
        ServiceComponentHostRequest req2;
        Set<ServiceComponentHostRequest> reqs = new HashSet<ServiceComponentHostRequest>();

        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("invalid stack id");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "Invalid desired stack id");

        c1.setCurrentStackVersion(null);
        sch1.setStackVersion(new StackId("HDP-0.1"));
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "Cluster has not been upgraded yet");

        c1.setCurrentStackVersion(new StackId("HDP2-0.1"));
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "Deployed stack name and requested stack names");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("HDP-0.3");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "Component host can only be upgraded to the same version");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.STARTED);
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "Component host is in an invalid state for upgrade");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.UPGRADING);
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.STARTED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "The desired state for an upgrade request must be");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.UPGRADING);
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1, null);
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        updateHostAndCompareExpectedFailure(reqs, "The desired state for an upgrade request must be");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.INSTALLED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.INSTALLED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        req2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host2,
                State.STARTED.toString());
        reqs.add(req2);
        updateHostAndCompareExpectedFailure(reqs, "An upgrade request cannot be combined with other");

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.INSTALLED);
        sch1.setStackVersion(new StackId("HDP-0.2"));
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1, null);
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);

        RequestStatusResponse resp = updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
        Assert.assertNull(resp);

        c1.setCurrentStackVersion(new StackId("HDP-0.2"));
        sch1.setState(State.INSTALLED);
        sch1.setStackVersion(new StackId("HDP-0.2"));
        reqs.clear();
        req1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1, host1,
                State.INSTALLED.toString());
        req1.setDesiredStackId("HDP-0.2");
        reqs.add(req1);
        resp = updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
        Assert.assertNull(resp);
    }

    private void updateHostAndCompareExpectedFailure(Set<ServiceComponentHostRequest> reqs,
            String expectedMessage) {
        try {
            updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
            fail("Expected failure: " + expectedMessage);
        } catch (Exception e) {
            LOG.info("Actual exception message: " + e.getMessage());
            Assert.assertTrue(e.getMessage().contains(expectedMessage));
        }
    }

    @Test
    public void testStartClientComponent() {
        // FIXME write test after meta data integration
        // start should fail
    }

    @Test
    public void testStartClientHostComponent() {
        // FIXME write test after meta data integration
        // start should fail
    }

    @Test
    public void testCreateCustomActions() throws Exception {
        setupClusterWithHosts("c1", "HDP-2.0.6", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
                add("h3");
            }
        }, "centos6");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.6"));
        cluster.setCurrentStackVersion(new StackId("HDP-2.0.6"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");

        cluster.addConfig(config1);
        cluster.addConfig(config2);

        Service hdfs = cluster.addService("HDFS");
        hdfs.persist();

        Service mapred = cluster.addService("YARN");
        mapred.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        hdfs.addServiceComponent(Role.NAMENODE.name()).persist();
        hdfs.addServiceComponent(Role.DATANODE.name()).persist();

        mapred.addServiceComponent(Role.RESOURCEMANAGER.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.NAMENODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h2").persist();

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a1", ActionType.SYSTEM,
                "test,[optional1]", "", "", "Does file exist", TargetHostType.SPECIFIC, Short.valueOf("100")));

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a2", ActionType.SYSTEM, "", "HDFS",
                "DATANODE", "Does file exist", TargetHostType.ALL, Short.valueOf("100")));

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");
        requestProperties.put("datanode", "abc");

        ArrayList<String> hosts = new ArrayList<String>() {
            {
                add("h1");
            }
        };
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", "DATANODE", hosts);
        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        resourceFilters.add(resourceFilter);

        ExecuteActionRequest actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null,
                params, false);
        RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);
        assertEquals(1, response.getTasks().size());
        ShortTaskStatus taskStatus = response.getTasks().get(0);
        Assert.assertEquals("h1", taskStatus.getHostName());

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Stage stage = actionDB.getAllStages(response.getRequestId()).get(0);
        Assert.assertNotNull(stage);

        Assert.assertEquals(1, storedTasks.size());
        HostRoleCommand task = storedTasks.get(0);
        Assert.assertEquals(RoleCommand.ACTIONEXECUTE, task.getRoleCommand());
        Assert.assertEquals("a1", task.getRole().name());
        Assert.assertEquals("h1", task.getHostName());
        ExecutionCommand cmd = task.getExecutionCommandWrapper().getExecutionCommand();
        Type type = new TypeToken<Map<String, String>>() {
        }.getType();
        Map<String, String> hostParametersStage = StageUtils.getGson().fromJson(stage.getHostParamsStage(), type);
        Map<String, String> commandParametersStage = StageUtils.getGson().fromJson(stage.getCommandParamsStage(),
                type);

        Assert.assertTrue(commandParametersStage.containsKey("test"));
        Assert.assertEquals("HDFS", cmd.getServiceName());
        Assert.assertEquals("DATANODE", cmd.getComponentName());
        Assert.assertNotNull(hostParametersStage.get("jdk_location"));

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("", "", null);
        resourceFilters.add(resourceFilter);
        actionRequest = new ExecuteActionRequest("c1", null, "a2", resourceFilters, null, params, false);
        response = controller.createAction(actionRequest, requestProperties);
        assertEquals(2, response.getTasks().size());

        final List<HostRoleCommand> storedTasks2 = actionDB.getRequestTasks(response.getRequestId());
        task = storedTasks2.get(1);
        Assert.assertEquals(RoleCommand.ACTIONEXECUTE, task.getRoleCommand());
        Assert.assertEquals("a2", task.getRole().name());
        HashSet<String> expectedHosts = new HashSet<String>() {
            {
                add("h2");
                add("h1");
            }
        };
        HashSet<String> actualHosts = new HashSet<String>() {
            {
                add(storedTasks2.get(1).getHostName());
                add(storedTasks2.get(0).getHostName());
            }
        };
        Assert.assertEquals(expectedHosts, actualHosts);

        cmd = task.getExecutionCommandWrapper().getExecutionCommand();
        commandParametersStage = StageUtils.getGson().fromJson(stage.getCommandParamsStage(), type);

        Assert.assertTrue(commandParametersStage.containsKey("test"));
        Assert.assertEquals("HDFS", cmd.getServiceName());
        Assert.assertEquals("DATANODE", cmd.getComponentName());

        hosts = new ArrayList<String>() {
            {
                add("h3");
            }
        };
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("", "", hosts);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null, params, false);
        response = controller.createAction(actionRequest, requestProperties);
        assertEquals(1, response.getTasks().size());
        taskStatus = response.getTasks().get(0);
        Assert.assertEquals("h3", taskStatus.getHostName());
    }

    @Test
    public void testComponentCategorySentWithRestart() throws AmbariException {
        setupClusterWithHosts("c1", "HDP-2.0.7", new ArrayList<String>() {
            {
                add("h1");
            }
        }, "centos5");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.7"));
        cluster.setCurrentStackVersion(new StackId("HDP-2.0.7"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");

        cluster.addConfig(config1);
        cluster.addConfig(config2);

        Service hdfs = cluster.addService("HDFS");
        hdfs.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        hdfs.addServiceComponent(Role.NAMENODE.name()).persist();
        hdfs.addServiceComponent(Role.DATANODE.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.NAMENODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h1").persist();

        installService("c1", "HDFS", false, false);

        startService("c1", "HDFS", false, false);

        Cluster c = clusters.getCluster("c1");
        Service s = c.getService("HDFS");

        Assert.assertEquals(State.STARTED, s.getDesiredState());
        for (ServiceComponent sc : s.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (sc.isClientComponent()) {
                    Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                } else {
                    Assert.assertEquals(State.STARTED, sch.getDesiredState());
                }
            }
        }

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", "HDFS_CLIENT",
                new ArrayList<String>() {
                    {
                        add("h1");
                    }
                });
        ExecuteActionRequest actionRequest = new ExecuteActionRequest("c1", "RESTART", params, false);
        actionRequest.getResourceFilters().add(resourceFilter);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");
        requestProperties.put("hdfs_client", "abc");

        RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);

        List<Stage> stages = actionDB.getAllStages(response.getRequestId());
        Assert.assertNotNull(stages);

        HostRoleCommand hrc = null;
        Type type = new TypeToken<Map<String, String>>() {
        }.getType();
        for (Stage stage : stages) {
            for (HostRoleCommand cmd : stage.getOrderedHostRoleCommands()) {
                if (cmd.getRole().equals(Role.HDFS_CLIENT)) {
                    hrc = cmd;
                }
                Map<String, String> hostParamStage = StageUtils.getGson().fromJson(stage.getHostParamsStage(),
                        type);
                Assert.assertTrue(hostParamStage.containsKey(ExecutionCommand.KeyNames.DB_DRIVER_FILENAME));
                Assert.assertTrue(hostParamStage.containsKey(ExecutionCommand.KeyNames.MYSQL_JDBC_URL));
                Assert.assertTrue(hostParamStage.containsKey(ExecutionCommand.KeyNames.ORACLE_JDBC_URL));
            }
        }
        Assert.assertNotNull(hrc);
        Assert.assertEquals("RESTART HDFS/HDFS_CLIENT", hrc.getCommandDetail());
        Map<String, String> roleParams = hrc.getExecutionCommandWrapper().getExecutionCommand().getRoleParams();

        Assert.assertNotNull(roleParams);
        Assert.assertEquals("CLIENT", roleParams.get(ExecutionCommand.KeyNames.COMPONENT_CATEGORY));
        Assert.assertTrue(hrc.getExecutionCommandWrapper().getExecutionCommand().getCommandParams()
                .containsKey("hdfs_client"));
        Assert.assertEquals("abc",
                hrc.getExecutionCommandWrapper().getExecutionCommand().getCommandParams().get("hdfs_client"));
    }

    @SuppressWarnings("serial")
    @Test
    public void testCreateActionsFailures() throws Exception {
        setupClusterWithHosts("c1", "HDP-2.0.7", new ArrayList<String>() {
            {
                add("h1");
            }
        }, "centos5");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.7"));
        cluster.setCurrentStackVersion(new StackId("HDP-2.0.7"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");
        config1.persist();
        config2.persist();

        cluster.addConfig(config1);
        cluster.addConfig(config2);
        cluster.addDesiredConfig("_test", Collections.singleton(config1));
        cluster.addDesiredConfig("_test", Collections.singleton(config2));

        Service hdfs = cluster.addService("HDFS");
        hdfs.persist();

        Service hive = cluster.addService("HIVE");
        hive.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        hdfs.addServiceComponent(Role.NAMENODE.name()).persist();
        hdfs.addServiceComponent(Role.DATANODE.name()).persist();

        hive.addServiceComponent(Role.HIVE_SERVER.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.NAMENODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h1").persist();

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };

        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", null, null);

        ExecuteActionRequest actionRequest = new ExecuteActionRequest("c1", "NON_EXISTENT_CHECK", params, false);
        actionRequest.getResourceFilters().add(resourceFilter);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        expectActionCreationErrorWithMessage(actionRequest, requestProperties, "Unsupported action");

        //actionRequest = new ExecuteActionRequest("c1", "NON_EXISTENT_SERVICE_CHECK", "HDFS", params);
        //expectActionCreationErrorWithMessage(actionRequest, requestProperties, "Unsupported action");

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION_DATANODE", params, false);
        actionRequest.getResourceFilters().add(resourceFilter);

        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Unsupported action DECOMMISSION_DATANODE for Service: HDFS and Component: null");

        //actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", "HDFS", params);
        //expectActionCreationErrorWithMessage(actionRequest, requestProperties, "Unsupported action DECOMMISSION for Service: HDFS and Component: null");

        resourceFilter = new RequestResourceFilter("HDFS", "HDFS_CLIENT", null);
        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params, false);

        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Unsupported action DECOMMISSION for Service: HDFS and Component: HDFS_CLIENT");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", null, null);
        resourceFilters.add(resourceFilter);
        actionRequest = new ExecuteActionRequest("c1", null, "DECOMMISSION_DATANODE", resourceFilters, null, params,
                false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action DECOMMISSION_DATANODE does not exist");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("YARN", "RESOURCEMANAGER", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Service not found, clusterName=c1, serviceName=YARN");

        Map<String, String> params2 = new HashMap<String, String>() {
            {
                put("included_hosts", "h1,h2");
                put("excluded_hosts", "h1,h3");
            }
        };

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params2, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Same host cannot be specified for inclusion as well as exclusion. Hosts: [h1]");

        params2 = new HashMap<String, String>() {
            {
                put("included_hosts", " h1,h2");
                put("excluded_hosts", "h4, h3");
                put("slave_type", "HDFS_CLIENT");
            }
        };
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params2, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Component HDFS_CLIENT is not supported for decommissioning.");

        List<String> hosts = new ArrayList<String>();
        hosts.add("h6");
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", hosts);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params2, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Decommission command cannot be issued with target host(s) specified.");

        hdfs.getServiceComponent(Role.DATANODE.name()).getServiceComponentHost("h1").setState(State.INSTALLED);
        params2 = new HashMap<String, String>() {
            {
                put("excluded_hosts", "h1 ");
            }
        };
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params2, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Component DATANODE on host h1 cannot be decommissioned as its not in STARTED state");

        params2 = new HashMap<String, String>() {
            {
                put("excluded_hosts", "h1 ");
                put("mark_draining_only", "true");
            }
        };
        actionRequest = new ExecuteActionRequest("c1", "DECOMMISSION", null, resourceFilters, null, params2, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "mark_draining_only is not a valid parameter for NAMENODE");

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a1", ActionType.SYSTEM,
                "test,dirName", "", "", "Does file exist", TargetHostType.SPECIFIC, Short.valueOf("100")));

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a2", ActionType.SYSTEM, "", "HDFS",
                "DATANODE", "Does file exist", TargetHostType.ANY, Short.valueOf("100")));

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a3", ActionType.SYSTEM, "",
                "MAPREDUCE", "MAPREDUCE_CLIENT", "Does file exist", TargetHostType.ANY, Short.valueOf("100")));

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a4", ActionType.SYSTEM, "", "HIVE",
                "", "Does file exist", TargetHostType.ANY, Short.valueOf("100")));

        actionRequest = new ExecuteActionRequest("c1", null, "a1", null, null, null, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 requires input 'test' that is not provided");

        actionRequest = new ExecuteActionRequest("c1", null, "a1", null, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 requires input 'dirName' that is not provided");

        params.put("dirName", "dirName");
        actionRequest = new ExecuteActionRequest("c1", null, "a1", null, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 requires explicit target host(s)");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HIVE", null, null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a2", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a2 targets service HIVE that does not match with expected HDFS");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "HDFS_CLIENT", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a2", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a2 targets component HDFS_CLIENT that does not match with expected DATANODE");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS2", "HDFS_CLIENT", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 targets service HDFS2 that does not exist");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", "HDFS_CLIENT2", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 targets component HDFS_CLIENT2 that does not exist");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("", "HDFS_CLIENT2", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a1 targets component HDFS_CLIENT2 without specifying the target service");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("", "", null);
        resourceFilters.add(resourceFilter);

        // targets a service that is not a member of the stack (e.g. MR not in HDP-2)
        actionRequest = new ExecuteActionRequest("c1", null, "a3", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Action a3 targets service MAPREDUCE that does not exist");

        hosts = new ArrayList<String>();
        hosts.add("h6");
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("", "", hosts);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a2", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Request specifies host h6 but its not a valid host based on the target service=HDFS and component=DATANODE");

        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HIVE", "", null);
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest("c1", null, "a4", resourceFilters, null, params, false);
        expectActionCreationErrorWithMessage(actionRequest, requestProperties,
                "Suitable hosts not found, component=, service=HIVE, cluster=c1, actionName=a4");

    }

    private void expectActionCreationErrorWithMessage(ExecuteActionRequest actionRequest,
            Map<String, String> requestProperties, String message) {
        try {
            RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);
            Assert.fail("createAction should fail");
        } catch (AmbariException ex) {
            LOG.info(ex.getMessage());
            Assert.assertTrue(ex.getMessage().contains(message));
        }
    }

    @SuppressWarnings("serial")
    @Test
    public void testCreateServiceCheckActions() throws Exception {
        setupClusterWithHosts("c1", "HDP-0.1", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
            }
        }, "centos5");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        cluster.setCurrentStackVersion(new StackId("HDP-0.1"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");
        config1.setPropertiesAttributes(new HashMap<String, Map<String, String>>() {
            {
                put("attr1", new HashMap<String, String>());
            }
        });

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");
        config2.setPropertiesAttributes(new HashMap<String, Map<String, String>>() {
            {
                put("attr2", new HashMap<String, String>());
            }
        });

        cluster.addConfig(config1);
        cluster.addConfig(config2);
        cluster.addDesiredConfig("_test", Collections.singleton(config1));
        cluster.addDesiredConfig("_test", Collections.singleton(config2));

        Service hdfs = cluster.addService("HDFS");
        Service mapReduce = cluster.addService("MAPREDUCE");
        hdfs.persist();
        mapReduce.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        mapReduce.addServiceComponent(Role.MAPREDUCE_CLIENT.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        mapReduce.getServiceComponent(Role.MAPREDUCE_CLIENT.name()).addServiceComponentHost("h2").persist();

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };
        ExecuteActionRequest actionRequest = new ExecuteActionRequest("c1", Role.HDFS_SERVICE_CHECK.name(), params,
                false);
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", null, null);
        actionRequest.getResourceFilters().add(resourceFilter);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);

        assertEquals(1, response.getTasks().size());
        ShortTaskStatus task = response.getTasks().get(0);

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Stage stage = actionDB.getAllStages(response.getRequestId()).get(0);

        //Check configs not stored with execution command
        ExecutionCommandDAO executionCommandDAO = injector.getInstance(ExecutionCommandDAO.class);
        ExecutionCommandEntity commandEntity = executionCommandDAO.findByPK(task.getTaskId());

        Gson gson = new Gson();
        ExecutionCommand executionCommand = gson.fromJson(new StringReader(new String(commandEntity.getCommand())),
                ExecutionCommand.class);

        assertFalse(executionCommand.getConfigurationTags().isEmpty());
        assertTrue(executionCommand.getConfigurations() == null || executionCommand.getConfigurations().isEmpty());

        assertEquals(1, storedTasks.size());
        HostRoleCommand hostRoleCommand = storedTasks.get(0);

        assertEquals("SERVICE_CHECK HDFS", hostRoleCommand.getCommandDetail());
        assertNull(hostRoleCommand.getCustomCommandName());

        assertEquals(task.getTaskId(), hostRoleCommand.getTaskId());
        assertNotNull(actionRequest.getResourceFilters());
        RequestResourceFilter requestResourceFilter = actionRequest.getResourceFilters().get(0);
        assertEquals(resourceFilter.getServiceName(),
                hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getServiceName());
        assertEquals(actionRequest.getClusterName(),
                hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getClusterName());
        assertEquals(actionRequest.getCommandName(),
                hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getRole());
        assertEquals(Role.HDFS_CLIENT.name(), hostRoleCommand.getEvent().getEvent().getServiceComponentName());
        assertEquals(actionRequest.getParameters(),
                hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getRoleParams());
        assertNotNull(hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getConfigurations());
        assertEquals(2,
                hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getConfigurations().size());
        assertEquals(requestProperties.get(REQUEST_CONTEXT_PROPERTY), stage.getRequestContext());

        actionRequest = new ExecuteActionRequest("c1", Role.MAPREDUCE_SERVICE_CHECK.name(), null, false);
        resourceFilter = new RequestResourceFilter("MAPREDUCE", null, null);
        actionRequest.getResourceFilters().add(resourceFilter);

        injector.getInstance(ActionMetadata.class).addServiceCheckAction("MAPREDUCE");
        response = controller.createAction(actionRequest, requestProperties);

        assertEquals(1, response.getTasks().size());

        List<HostRoleCommand> tasks = actionDB.getRequestTasks(response.getRequestId());

        assertEquals(1, tasks.size());

        requestProperties.put(REQUEST_CONTEXT_PROPERTY, null);
        response = controller.createAction(actionRequest, requestProperties);

        assertEquals(1, response.getTasks().size());
    }

    private void createUser(String userName) throws Exception {
        UserRequest request = new UserRequest(userName);
        request.setPassword("password");

        controller.createUsers(new HashSet<UserRequest>(Collections.singleton(request)));
    }

    @Test
    public void testCreateAndGetUsers() throws Exception {
        createUser("user1");

        Set<UserResponse> r = controller.getUsers(Collections.singleton(new UserRequest("user1")));

        Assert.assertEquals(1, r.size());
        UserResponse resp = r.iterator().next();
        Assert.assertEquals("user1", resp.getUsername());
    }

    @Test
    public void testGetUsers() throws Exception {
        createUser("user1");
        createUser("user2");
        createUser("user3");

        UserRequest request = new UserRequest(null);

        Set<UserResponse> responses = controller.getUsers(Collections.singleton(request));

        Assert.assertEquals(3, responses.size());
    }

    @SuppressWarnings("serial")
    @Test
    public void testUpdateUsers() throws Exception {
        createUser("user1");

        UserRequest request = new UserRequest("user1");

        controller.updateUsers(Collections.singleton(request));
    }

    @SuppressWarnings("serial")
    @Ignore
    @Test
    public void testDeleteUsers() throws Exception {
        createUser("user1");

        UserRequest request = new UserRequest("user1");
        controller.updateUsers(Collections.singleton(request));

        request = new UserRequest("user1");
        controller.deleteUsers(Collections.singleton(request));

        Set<UserResponse> responses = controller.getUsers(Collections.singleton(new UserRequest(null)));

        Assert.assertEquals(0, responses.size());
    }

    @Test
    public void testRcaOnJobtrackerHost() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "MAPREDUCE";
        createService(clusterName, serviceName, null);
        String componentName1 = "JOBTRACKER";
        String componentName2 = "TASKTRACKER";
        String componentName3 = "MAPREDUCE_CLIENT";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");
        configs.put("rca_enabled", "true");

        ClusterRequest cr = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        cr.setDesiredConfig(
                Collections.singletonList(new ConfigurationRequest(clusterName, "global", "v1", configs, null)));
        controller.updateClusters(Collections.singleton(cr), Collections.<String, String>emptyMap());

        Set<ServiceRequest> sReqs = new HashSet<ServiceRequest>();
        Map<String, String> configVersions = new HashMap<String, String>();
        configVersions.put("global", "v1");
        sReqs.clear();
        sReqs.add(new ServiceRequest(clusterName, serviceName, State.INSTALLED.name()));
        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, sReqs,
                mapRequestProps, true, false);
        List<Stage> stages = actionDB.getAllStages(trackAction.getRequestId());
        for (ExecutionCommandWrapper cmd : stages.get(0).getExecutionCommands(host1)) {
            assertEquals("true", cmd.getExecutionCommand().getConfigurations().get("global").get("rca_enabled"));
        }
        for (ExecutionCommandWrapper cmd : stages.get(0).getExecutionCommands(host2)) {
            assertEquals("false", cmd.getExecutionCommand().getConfigurations().get("global").get("rca_enabled"));
        }
    }

    @Test
    public void testUpdateConfigForRunningService() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host2));

        // Install
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // manually change live state to installed as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        // Start
        r = new ServiceRequest(clusterName, serviceName, State.STARTED.toString());
        requests.clear();
        requests.add(r);
        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);

        // manually change live state to started as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.STARTED);
            }
        }

        Assert.assertEquals(State.STARTED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            if (sc.getName().equals("HDFS_CLIENT")) {
                Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            } else {
                Assert.assertEquals(State.STARTED, sc.getDesiredState());
            }
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (sch.getServiceComponentName().equals("HDFS_CLIENT")) {
                    Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                } else {
                    Assert.assertEquals(State.STARTED, sch.getDesiredState());
                }
            }
        }

        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2, cr3, cr4, cr5, cr6, cr7, cr8;
        cr1 = new ConfigurationRequest(clusterName, "typeA", "v1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "typeB", "v1", configs, null);
        cr3 = new ConfigurationRequest(clusterName, "typeC", "v1", configs, null);
        cr4 = new ConfigurationRequest(clusterName, "typeD", "v1", configs, null);
        cr5 = new ConfigurationRequest(clusterName, "typeA", "v2", configs, null);
        cr6 = new ConfigurationRequest(clusterName, "typeB", "v2", configs, null);
        cr7 = new ConfigurationRequest(clusterName, "typeC", "v2", configs, null);
        cr8 = new ConfigurationRequest(clusterName, "typeE", "v1", configs, null);
        controller.createConfiguration(cr1);
        controller.createConfiguration(cr2);
        controller.createConfiguration(cr3);
        controller.createConfiguration(cr4);
        controller.createConfiguration(cr5);
        controller.createConfiguration(cr6);
        controller.createConfiguration(cr7);
        controller.createConfiguration(cr8);

        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        ServiceComponent sc1 = s.getServiceComponent(componentName1);
        ServiceComponent sc2 = s.getServiceComponent(componentName2);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);

        Set<ServiceComponentHostRequest> schReqs = new HashSet<ServiceComponentHostRequest>();
        Set<ServiceComponentRequest> scReqs = new HashSet<ServiceComponentRequest>();
        Set<ServiceRequest> sReqs = new HashSet<ServiceRequest>();
        Map<String, String> configVersions = new HashMap<String, String>();

        // update configs at SCH and SC level
        configVersions.clear();
        configVersions.put("typeA", "v1");
        configVersions.put("typeB", "v1");
        configVersions.put("typeC", "v1");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        configVersions.clear();
        configVersions.put("typeC", "v1");
        configVersions.put("typeD", "v1");
        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName2, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        // update configs at service level
        configVersions.clear();
        configVersions.put("typeA", "v2");
        configVersions.put("typeC", "v2");
        configVersions.put("typeE", "v1");
        sReqs.clear();
        sReqs.add(new ServiceRequest(clusterName, serviceName, null));
        Assert.assertNull(
                ServiceResourceProviderTest.updateServices(controller, sReqs, mapRequestProps, true, false));

        // update configs at SCH level
        configVersions.clear();
        configVersions.put("typeA", "v1");
        configVersions.put("typeB", "v1");
        configVersions.put("typeC", "v1");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        // update configs at SC level
        configVersions.clear();
        configVersions.put("typeC", "v2");
        configVersions.put("typeD", "v1");
        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName1, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

    }

    @Test
    public void testConfigUpdates() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host2));

        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        Map<String, Map<String, String>> configAttributes = new HashMap<String, Map<String, String>>();
        configAttributes.put("final", new HashMap<String, String>());
        configAttributes.get("final").put("a", "true");

        ConfigurationRequest cr1, cr2, cr3, cr4, cr5, cr6, cr7, cr8;
        cr1 = new ConfigurationRequest(clusterName, "typeA", "v1", configs, configAttributes);
        cr2 = new ConfigurationRequest(clusterName, "typeB", "v1", configs, configAttributes);
        cr3 = new ConfigurationRequest(clusterName, "typeC", "v1", configs, configAttributes);
        cr4 = new ConfigurationRequest(clusterName, "typeD", "v1", configs, configAttributes);
        cr5 = new ConfigurationRequest(clusterName, "typeA", "v2", configs, configAttributes);
        cr6 = new ConfigurationRequest(clusterName, "typeB", "v2", configs, configAttributes);
        cr7 = new ConfigurationRequest(clusterName, "typeC", "v2", configs, configAttributes);
        cr8 = new ConfigurationRequest(clusterName, "typeE", "v1", configs, configAttributes);
        controller.createConfiguration(cr1);
        controller.createConfiguration(cr2);
        controller.createConfiguration(cr3);
        controller.createConfiguration(cr4);
        controller.createConfiguration(cr5);
        controller.createConfiguration(cr6);
        controller.createConfiguration(cr7);
        controller.createConfiguration(cr8);

        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        ServiceComponent sc1 = s.getServiceComponent(componentName1);
        ServiceComponent sc2 = s.getServiceComponent(componentName2);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);

        Set<ServiceComponentHostRequest> schReqs = new HashSet<ServiceComponentHostRequest>();
        Set<ServiceComponentRequest> scReqs = new HashSet<ServiceComponentRequest>();
        Set<ServiceRequest> sReqs = new HashSet<ServiceRequest>();
        Map<String, String> configVersions = new HashMap<String, String>();

        // update configs at SCH and SC level
        configVersions.clear();
        configVersions.put("typeA", "v1");
        configVersions.put("typeB", "v1");
        configVersions.put("typeC", "v1");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        configVersions.clear();
        configVersions.put("typeC", "v1");
        configVersions.put("typeD", "v1");
        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName2, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        // update configs at service level
        configVersions.clear();
        configVersions.put("typeA", "v2");
        configVersions.put("typeC", "v2");
        configVersions.put("typeE", "v1");
        sReqs.clear();
        sReqs.add(new ServiceRequest(clusterName, serviceName, null));
        Assert.assertNull(
                ServiceResourceProviderTest.updateServices(controller, sReqs, mapRequestProps, true, false));

        // update configs at SCH level
        configVersions.clear();
        configVersions.put("typeA", "v1");
        configVersions.put("typeB", "v1");
        configVersions.put("typeC", "v1");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        // update configs at SC level
        configVersions.clear();
        configVersions.put("typeC", "v2");
        configVersions.put("typeD", "v1");
        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName1, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

    }

    @Test
    public void testReConfigureService() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Install
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // manually change live state to installed as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2, cr3;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);
        cr3 = new ConfigurationRequest(clusterName, "core-site", "version122", configs, null);
        controller.createConfiguration(cr1);
        controller.createConfiguration(cr2);
        controller.createConfiguration(cr3);

        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        ServiceComponent sc1 = s.getServiceComponent(componentName1);
        ServiceComponent sc2 = s.getServiceComponent(componentName2);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);

        Set<ServiceComponentHostRequest> schReqs = new HashSet<ServiceComponentHostRequest>();
        Set<ServiceComponentRequest> scReqs = new HashSet<ServiceComponentRequest>();
        Set<ServiceRequest> sReqs = new HashSet<ServiceRequest>();
        Map<String, String> configVersions = new HashMap<String, String>();

        // SCH level
        configVersions.clear();
        configVersions.put("core-site", "version1");
        configVersions.put("hdfs-site", "version1");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        // Reconfigure SCH level
        configVersions.clear();
        configVersions.put("core-site", "version122");
        schReqs.clear();
        schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));

        // Clear Entity Manager
        entityManager.clear();

        //SC Level
        configVersions.clear();
        configVersions.put("core-site", "version1");
        configVersions.put("hdfs-site", "version1");
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName2, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName1, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        // Reconfigure SC level
        configVersions.clear();
        configVersions.put("core-site", "version122");

        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName2, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        scReqs.clear();
        scReqs.add(new ServiceComponentRequest(clusterName, serviceName, componentName1, null));
        Assert.assertNull(ComponentResourceProviderTest.updateComponents(controller, scReqs,
                Collections.<String, String>emptyMap(), true));

        entityManager.clear();

        // S level
        configVersions.clear();
        configVersions.put("core-site", "version1");
        configVersions.put("hdfs-site", "version1");
        sReqs.clear();
        sReqs.add(new ServiceRequest(clusterName, serviceName, null));
        Assert.assertNull(
                ServiceResourceProviderTest.updateServices(controller, sReqs, mapRequestProps, true, false));

        // Reconfigure S Level
        configVersions.clear();
        configVersions.put("core-site", "version122");

        sReqs.clear();
        sReqs.add(new ServiceRequest(clusterName, serviceName, null));
        Assert.assertNull(
                ServiceResourceProviderTest.updateServices(controller, sReqs, mapRequestProps, true, false));

        entityManager.clear();

    }

    @Test
    public void testReConfigureServiceClient() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName1 = "HDFS";
        String serviceName2 = "MAPREDUCE";
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        String componentName4 = "JOBTRACKER";
        String componentName5 = "TASKTRACKER";
        String componentName6 = "MAPREDUCE_CLIENT";

        createService(clusterName, serviceName1, null);
        createService(clusterName, serviceName2, null);

        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName3, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName4, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName5, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName6, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName1, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName1, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName2, componentName4, host1, null);
        createServiceComponentHost(clusterName, serviceName2, componentName5, host1, null);
        createServiceComponentHost(clusterName, serviceName1, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName1, componentName3, host2, null);
        createServiceComponentHost(clusterName, serviceName2, componentName6, host2, null);
        createServiceComponentHost(clusterName, serviceName1, componentName3, host3, null);
        createServiceComponentHost(clusterName, serviceName2, componentName6, host3, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");
        Map<String, String> configs2 = new HashMap<String, String>();
        configs2.put("c", "d");

        ConfigurationRequest cr1, cr2, cr3;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);

        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr2));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Install
        long requestId1 = installService(clusterName, serviceName1, true, false);

        List<Stage> stages = actionDB.getAllStages(requestId1);
        Assert.assertEquals(2, stages.get(0).getOrderedHostRoleCommands().get(0).getExecutionCommandWrapper()
                .getExecutionCommand().getConfigurationTags().size());

        installService(clusterName, serviceName2, false, false);

        // Start
        startService(clusterName, serviceName1, true, false);
        startService(clusterName, serviceName2, true, false);

        // Reconfigure
        cr3 = new ConfigurationRequest(clusterName, "core-site", "version122", configs2, null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr3));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Stop HDFS & MAPREDUCE
        stopService(clusterName, serviceName1, false, false);
        stopService(clusterName, serviceName2, false, false);

        // Start
        long requestId2 = startService(clusterName, serviceName1, true, true);
        long requestId3 = startService(clusterName, serviceName2, true, true);

        stages = actionDB.getAllStages(requestId2);
        stages.addAll(actionDB.getAllStages(requestId3));
        HostRoleCommand hdfsCmdHost3 = null;
        HostRoleCommand hdfsCmdHost2 = null;
        HostRoleCommand mapRedCmdHost2 = null;
        HostRoleCommand mapRedCmdHost3 = null;
        for (Stage stage : stages) {
            List<HostRoleCommand> hrcs = stage.getOrderedHostRoleCommands();

            for (HostRoleCommand hrc : hrcs) {
                LOG.debug("role: " + hrc.getRole());
                if (hrc.getRole().toString().equals("HDFS_CLIENT")) {
                    if (hrc.getHostName().equals(host3)) {
                        hdfsCmdHost3 = hrc;
                    } else if (hrc.getHostName().equals(host2)) {
                        hdfsCmdHost2 = hrc;
                    }
                }
                if (hrc.getRole().toString().equals("MAPREDUCE_CLIENT")) {
                    if (hrc.getHostName().equals(host2)) {
                        mapRedCmdHost2 = hrc;
                    } else if (hrc.getHostName().equals(host3)) {
                        mapRedCmdHost3 = hrc;
                    }
                }
            }
        }
        Assert.assertNotNull(hdfsCmdHost3);
        Assert.assertNotNull(hdfsCmdHost2);
        ExecutionCommand execCmd = hdfsCmdHost3.getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertEquals(2, execCmd.getConfigurationTags().size());
        Assert.assertEquals("version122", execCmd.getConfigurationTags().get("core-site").get("tag"));
        Assert.assertEquals("d", execCmd.getConfigurations().get("core-site").get("c"));
        // Check if MapReduce client is reinstalled
        Assert.assertNotNull(mapRedCmdHost2);
        Assert.assertNotNull(mapRedCmdHost3);

        /**
         * Test for lost host
         */
        // Stop HDFS & MAPREDUCE
        stopService(clusterName, serviceName1, false, false);
        stopService(clusterName, serviceName2, false, false);

        clusters.getHost(host2).setState(HostState.HEARTBEAT_LOST);

        // Start MAPREDUCE, HDFS is started as a dependency
        requestId3 = startService(clusterName, serviceName2, true, true);
        stages = actionDB.getAllStages(requestId3);
        HostRoleCommand clientWithHostDown = null;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().toString().equals("MAPREDUCE_CLIENT") && hrc.getHostName().equals(host2)) {
                    clientWithHostDown = hrc;
                }
            }
        }
        Assert.assertNull(clientWithHostDown);

        Assert.assertEquals(State.STARTED, clusters.getCluster(clusterName).getService("MAPREDUCE")
                .getServiceComponent("TASKTRACKER").getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.STARTED, clusters.getCluster(clusterName).getService("HDFS")
                .getServiceComponent("NAMENODE").getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.STARTED, clusters.getCluster(clusterName).getService("HDFS")
                .getServiceComponent("DATANODE").getServiceComponentHost(host1).getState());
    }

    @Test
    public void testReconfigureClientWithServiceStarted() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");
        Map<String, String> configs2 = new HashMap<String, String>();
        configs2.put("c", "d");

        ConfigurationRequest cr1, cr2, cr3;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);

        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr2));
        controller.updateClusters(Collections.singleton(crReq), null);

        installService(clusterName, serviceName, false, false);
        startService(clusterName, serviceName, false, false);

        Cluster c = clusters.getCluster(clusterName);
        Service s = c.getService(serviceName);
        // Stop Sch only
        stopServiceComponentHosts(clusterName, serviceName);
        Assert.assertEquals(State.STARTED, s.getDesiredState());
        for (ServiceComponent sc : s.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
            }
        }

        // Reconfigure
        cr3 = new ConfigurationRequest(clusterName, "core-site", "version122", configs2, null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr3));
        controller.updateClusters(Collections.singleton(crReq), null);

        long id = startService(clusterName, serviceName, false, true);
        List<Stage> stages = actionDB.getAllStages(id);
        HostRoleCommand clientHrc = null;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getHostName().equals(host2) && hrc.getRole().toString().equals("HDFS_CLIENT")) {
                    clientHrc = hrc;
                    Assert.assertEquals("version122", hrc.getExecutionCommandWrapper().getExecutionCommand()
                            .getConfigurationTags().get("core-site").get("tag"));
                }
            }
        }
        Assert.assertNotNull(clientHrc);
    }

    @Test
    public void testClientServiceSmokeTests() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "PIG";
        createService(clusterName, serviceName, null);
        String componentName1 = "PIG";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, null, componentName1, host2, null);

        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                Assert.assertEquals(State.INIT, sch.getState());
            }
        }

        List<ShortTaskStatus> taskStatuses = trackAction.getTasks();
        Assert.assertEquals(2, taskStatuses.size());

        List<Stage> stages = actionDB.getAllStages(trackAction.getRequestId());
        Assert.assertEquals(1, stages.size());
        Assert.assertEquals("Called from a test", stages.get(0).getRequestContext());

        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        r = new ServiceRequest(clusterName, serviceName, State.STARTED.toString());
        requests.clear();
        requests.add(r);

        injector.getInstance(ActionMetadata.class).addServiceCheckAction("PIG");
        trackAction = ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true,
                false);
        Assert.assertNotNull(trackAction);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            Assert.assertEquals(State.INSTALLED, sc.getDesiredState());
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                Assert.assertEquals(State.INSTALLED, sch.getState());
            }
        }

        stages = actionDB.getAllStages(trackAction.getRequestId());
        for (Stage s : stages) {
            LOG.info("Stage dump : " + s.toString());
        }
        Assert.assertEquals(1, stages.size());

        taskStatuses = trackAction.getTasks();
        Assert.assertEquals(1, taskStatuses.size());
        Assert.assertEquals(Role.PIG_SERVICE_CHECK.toString(), taskStatuses.get(0).getRole());
    }

    @Test
    public void testSkipTaskOnUnhealthyHosts() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host3, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // h1=HEALTHY, h2=HEARTBEAT_LOST, h3=WAITING_FOR_HOST_STATUS_UPDATES
        clusters.getHost("h1").setState(HostState.HEALTHY);
        clusters.getHost("h2").setState(HostState.HEALTHY);
        clusters.getHost("h3").setState(HostState.HEARTBEAT_LOST);

        long requestId = startService(clusterName, serviceName, true, false);
        List<HostRoleCommand> commands = actionDB.getRequestTasks(requestId);
        Assert.assertEquals(3, commands.size());
        int commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.START) {
                Assert.assertTrue(command.getHostName().equals("h1") || command.getHostName().equals("h2"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only two task.", 2, commandCount);

        stopService(clusterName, serviceName, false, false);

        // h1=HEARTBEAT_LOST, h2=HEARTBEAT_LOST, h3=HEALTHY
        clusters.getHost("h1").setState(HostState.HEARTBEAT_LOST);
        clusters.getHost("h2").setState(HostState.HEARTBEAT_LOST);
        clusters.getHost("h3").setState(HostState.HEALTHY);

        requestId = startService(clusterName, serviceName, true, false);
        commands = actionDB.getRequestTasks(requestId);
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.START) {
                Assert.assertTrue(command.getHostName().equals("h3"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one task.", 1, commandCount);

        stopService(clusterName, serviceName, false, false);

        // h1=HEALTHY, h2=HEALTHY, h3=HEALTHY
        clusters.getHost("h1").setState(HostState.HEALTHY);
        clusters.getHost("h2").setState(HostState.HEALTHY);
        clusters.getHost("h3").setState(HostState.HEALTHY);

        requestId = startService(clusterName, serviceName, true, false);
        commands = actionDB.getRequestTasks(requestId);
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.START) {
                Assert.assertTrue(command.getHostName().equals("h3") || command.getHostName().equals("h2")
                        || command.getHostName().equals("h1"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect all three task.", 3, commandCount);

        // h1=HEALTHY, h2=HEARTBEAT_LOST, h3=HEALTHY
        clusters.getHost("h2").setState(HostState.HEARTBEAT_LOST);
        requestId = stopService(clusterName, serviceName, false, false);
        commands = actionDB.getRequestTasks(requestId);
        Assert.assertEquals(2, commands.size());
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.STOP) {
                Assert.assertTrue(command.getHostName().equals("h3") || command.getHostName().equals("h1"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only two task.", 2, commandCount);

        // Force a sch into INSTALL_FAILED
        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        ServiceComponent sc3 = s.getServiceComponent(componentName2);
        for (ServiceComponentHost sch : sc3.getServiceComponentHosts().values()) {
            if (sch.getHostName().equals("h3")) {
                sch.setState(State.INSTALL_FAILED);
            }
        }

        // h1=HEALTHY, h2=HEALTHY, h3=HEARTBEAT_LOST
        clusters.getHost("h3").setState(HostState.HEARTBEAT_LOST);
        clusters.getHost("h2").setState(HostState.HEALTHY);
        requestId = installService(clusterName, serviceName, false, false);
        Assert.assertEquals(-1, requestId);

        // All healthy, INSTALL should succeed
        clusters.getHost("h3").setState(HostState.HEALTHY);
        requestId = installService(clusterName, serviceName, false, false);
        commands = actionDB.getRequestTasks(requestId);
        Assert.assertEquals(1, commands.size());
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.INSTALL) {
                Assert.assertTrue(command.getHostName().equals("h3"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one task.", 1, commandCount);
    }

    @Test
    public void testServiceCheckWhenHostIsUnhealthy() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host3, null);

        // Install
        installService(clusterName, serviceName, false, false);
        clusters.getHost("h3").setState(HostState.UNHEALTHY);
        clusters.getHost("h2").setState(HostState.HEALTHY);

        // Start
        long requestId = startService(clusterName, serviceName, true, false);
        List<HostRoleCommand> commands = actionDB.getRequestTasks(requestId);
        int commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.SERVICE_CHECK
                    && command.getRole() == Role.HDFS_SERVICE_CHECK) {
                Assert.assertTrue(command.getHostName().equals("h2"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one service check.", 1, commandCount);

        stopService(clusterName, serviceName, false, false);

        clusters.getHost("h3").setState(HostState.HEALTHY);
        clusters.getHost("h2").setState(HostState.HEARTBEAT_LOST);

        requestId = startService(clusterName, serviceName, true, false);
        commands = actionDB.getRequestTasks(requestId);
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.SERVICE_CHECK
                    && command.getRole() == Role.HDFS_SERVICE_CHECK) {
                Assert.assertTrue(command.getHostName().equals("h3"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one service check.", 1, commandCount);

        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", null, null);
        ExecuteActionRequest actionRequest = new ExecuteActionRequest("foo1", Role.HDFS_SERVICE_CHECK.name(), null,
                false);
        actionRequest.getResourceFilters().add(resourceFilter);
        Map<String, String> requestProperties = new HashMap<String, String>();

        RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);
        commands = actionDB.getRequestTasks(response.getRequestId());
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.SERVICE_CHECK
                    && command.getRole() == Role.HDFS_SERVICE_CHECK) {
                Assert.assertTrue(command.getHostName().equals("h3"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one service check.", 1, commandCount);

        // When both are unhealthy then just pick one
        clusters.getHost("h3").setState(HostState.WAITING_FOR_HOST_STATUS_UPDATES);
        clusters.getHost("h2").setState(HostState.INIT);
        response = controller.createAction(actionRequest, requestProperties);
        commands = actionDB.getRequestTasks(response.getRequestId());
        commandCount = 0;
        for (HostRoleCommand command : commands) {
            if (command.getRoleCommand() == RoleCommand.SERVICE_CHECK
                    && command.getRole() == Role.HDFS_SERVICE_CHECK) {
                Assert.assertTrue(command.getHostName().equals("h3") || command.getHostName().equals("h2"));
                commandCount++;
            }
        }
        Assert.assertEquals("Expect only one service check.", 1, commandCount);
    }

    @Test
    public void testReInstallForInstallFailedClient() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host3, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Mark client as install failed.
        Cluster cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        ServiceComponent sc3 = s.getServiceComponent(componentName3);
        for (ServiceComponentHost sch : sc3.getServiceComponentHosts().values()) {
            if (sch.getHostName().equals(host3)) {
                sch.setState(State.INSTALL_FAILED);
            }
        }

        // Start
        long requestId = startService(clusterName, serviceName, false, true);
        List<Stage> stages = actionDB.getAllStages(requestId);
        HostRoleCommand clientReinstallCmd = null;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getHostName().equals(host3) && hrc.getRole().toString().equals("HDFS_CLIENT")) {
                    clientReinstallCmd = hrc;
                    break;
                }
            }
        }
        Assert.assertNotNull(clientReinstallCmd);
    }

    @Test
    public void testReInstallClientComponent() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-2.0.6"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host3, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Reinstall SCH
        ServiceComponentHostRequest schr = new ServiceComponentHostRequest(clusterName, serviceName, componentName3,
                host3, State.INSTALLED.name());
        Set<ServiceComponentHostRequest> setReqs = new HashSet<ServiceComponentHostRequest>();
        setReqs.add(schr);
        RequestStatusResponse resp = updateHostComponents(setReqs, Collections.<String, String>emptyMap(), false);

        Assert.assertNotNull(resp);
        Assert.assertTrue(resp.getRequestId() > 0);
        List<Stage> stages = actionDB.getAllStages(resp.getRequestId());
        HostRoleCommand clientReinstallCmd = null;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getHostName().equals(host3) && hrc.getRole().toString().equals("HDFS_CLIENT")) {
                    clientReinstallCmd = hrc;
                    break;
                }
            }
        }
        Assert.assertNotNull(clientReinstallCmd);
    }

    @Test
    public void testReInstallClientComponentFromServiceChange() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-2.0.6"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName, host2, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Start Service
        ServiceRequest sr = new ServiceRequest(clusterName, serviceName, State.STARTED.name());
        Set<ServiceRequest> setReqs = new HashSet<ServiceRequest>();
        setReqs.add(sr);
        RequestStatusResponse resp = ServiceResourceProviderTest.updateServices(controller, setReqs,
                Collections.<String, String>emptyMap(), false, true);

        Assert.assertNotNull(resp);
        Assert.assertTrue(resp.getRequestId() > 0);

        List<Stage> stages = actionDB.getAllStages(resp.getRequestId());
        Map<String, Role> hostsToRoles = new HashMap<String, Role>();
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                hostsToRoles.put(hrc.getHostName(), hrc.getRole());
            }
        }

        Map<String, Role> expectedHostsToRoles = new HashMap<String, Role>();
        expectedHostsToRoles.put(host1, Role.HDFS_CLIENT);
        expectedHostsToRoles.put(host2, Role.HDFS_CLIENT);
        Assert.assertEquals(expectedHostsToRoles, hostsToRoles);
    }

    @Test
    public void testDecommissonDatanodeAction() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.7"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        RequestOperationLevel level = new RequestOperationLevel(Resource.Type.HostComponent, clusterName, null,
                null, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1;
        cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);
        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Start
        startService(clusterName, serviceName, false, false);

        cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        Assert.assertEquals(State.STARTED, s.getDesiredState());
        ServiceComponentHost scHost = s.getServiceComponent("DATANODE").getServiceComponentHost("h2");
        Assert.assertEquals(HostComponentAdminState.INSERVICE, scHost.getComponentAdminState());

        // Decommission one datanode
        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
                put("excluded_hosts", "h2");
                put("align_maintenance_state", "true");
            }
        };
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        ArrayList<RequestResourceFilter> filters = new ArrayList<RequestResourceFilter>();
        filters.add(resourceFilter);
        ExecuteActionRequest request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, filters, level,
                params, false);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        RequestStatusResponse response = controller.createAction(request, requestProperties);

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        ExecutionCommand execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertNotNull(execCmd.getConfigurationTags().get("hdfs-site"));
        Assert.assertEquals(1, storedTasks.size());
        Assert.assertEquals(HostComponentAdminState.DECOMMISSIONED, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.ON, scHost.getMaintenanceState());
        HostRoleCommand command = storedTasks.get(0);
        Assert.assertEquals(Role.NAMENODE, command.getRole());
        Assert.assertEquals(RoleCommand.CUSTOM_COMMAND, command.getRoleCommand());
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));

        // Decommission the other datanode
        params = new HashMap<String, String>() {
            {
                put("test", "test");
                put("excluded_hosts", "h1");
                put("align_maintenance_state", "true");
            }
        };
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        filters = new ArrayList<RequestResourceFilter>();
        filters.add(resourceFilter);

        request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, filters, level, params, false);

        response = controller.createAction(request, requestProperties);

        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Map<String, String> cmdParams = execCmd.getCommandParams();
        Assert.assertTrue(cmdParams.containsKey("update_exclude_file_only"));
        Assert.assertTrue(cmdParams.get("update_exclude_file_only").equals("false"));
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(1, storedTasks.size());
        Assert.assertEquals(HostComponentAdminState.DECOMMISSIONED, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.ON, scHost.getMaintenanceState());
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));

        // Recommission the other datanode  (while adding NameNode HA)
        createServiceComponentHost(clusterName, serviceName, componentName1, host2, null);
        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, serviceName, componentName1,
                host2, State.INSTALLED.toString());
        Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
        requests.add(r);
        updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
        s.getServiceComponent(componentName1).getServiceComponentHost(host2).setState(State.INSTALLED);
        r = new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host2,
                State.STARTED.toString());
        requests.clear();
        requests.add(r);
        updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
        s.getServiceComponent(componentName1).getServiceComponentHost(host2).setState(State.STARTED);

        params = new HashMap<String, String>() {
            {
                put("test", "test");
                put("included_hosts", "h1 , h2");
                put("align_maintenance_state", "true");
            }
        };
        resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        filters = new ArrayList<RequestResourceFilter>();
        filters.add(resourceFilter);
        request = new ExecuteActionRequest(clusterName, "DECOMMISSION", null, filters, level, params, false);

        response = controller.createAction(request, requestProperties);

        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Assert.assertNotNull(storedTasks);
        scHost = s.getServiceComponent("DATANODE").getServiceComponentHost("h2");
        Assert.assertEquals(HostComponentAdminState.INSERVICE, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.OFF, scHost.getMaintenanceState());
        execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(2, storedTasks.size());
        int countRefresh = 0;
        for (HostRoleCommand hrc : storedTasks) {
            Assert.assertTrue("DECOMMISSION, Included: h1,h2".equals(hrc.getCommandDetail()));
            Assert.assertTrue("DECOMMISSION".equals(hrc.getCustomCommandName()));
            cmdParams = hrc.getExecutionCommandWrapper().getExecutionCommand().getCommandParams();
            if (!cmdParams.containsKey("update_exclude_file_only")
                    || !cmdParams.get("update_exclude_file_only").equals("true")) {
                countRefresh++;
            }
        }
        Assert.assertEquals(2, countRefresh);

        // Slave components will have admin state as INSERVICE even if the state in DB is null
        scHost.setComponentAdminState(null);
        Assert.assertEquals(HostComponentAdminState.INSERVICE, scHost.getComponentAdminState());
        Assert.assertEquals(MaintenanceState.OFF, scHost.getMaintenanceState());
    }

    @Test
    public void testResourceFiltersWithCustomActions() throws AmbariException {
        setupClusterWithHosts("c1", "HDP-2.0.6", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
                add("h3");
            }
        }, "centos6");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.6"));
        cluster.setCurrentStackVersion(new StackId("HDP-2.0.6"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");

        cluster.addConfig(config1);
        cluster.addConfig(config2);

        Service hdfs = cluster.addService("HDFS");
        hdfs.persist();

        Service mapred = cluster.addService("YARN");
        mapred.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        hdfs.addServiceComponent(Role.NAMENODE.name()).persist();
        hdfs.addServiceComponent(Role.DATANODE.name()).persist();

        mapred.addServiceComponent(Role.RESOURCEMANAGER.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.NAMENODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h2").persist();

        controller.getAmbariMetaInfo().addActionDefinition(new ActionDefinition("a1", ActionType.SYSTEM, "", "HDFS",
                "", "Some custom action.", TargetHostType.ALL, Short.valueOf("100")));

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        ArrayList<String> hosts = new ArrayList<String>() {
            {
                add("h2");
            }
        };
        RequestResourceFilter resourceFilter1 = new RequestResourceFilter("HDFS", "DATANODE", hosts);

        hosts = new ArrayList<String>() {
            {
                add("h1");
            }
        };
        RequestResourceFilter resourceFilter2 = new RequestResourceFilter("HDFS", "NAMENODE", hosts);

        resourceFilters.add(resourceFilter1);
        resourceFilters.add(resourceFilter2);

        ExecuteActionRequest actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null,
                params, false);
        RequestStatusResponse response = null;
        try {
            response = controller.createAction(actionRequest, requestProperties);
        } catch (AmbariException ae) {
            LOG.info("Expected exception.", ae);
            Assert.assertTrue(ae.getMessage()
                    .contains("Custom action definition only " + "allows one resource filter to be specified"));
        }
        resourceFilters.remove(resourceFilter1);
        actionRequest = new ExecuteActionRequest("c1", null, "a1", resourceFilters, null, params, false);
        response = controller.createAction(actionRequest, requestProperties);

        assertEquals(1, response.getTasks().size());
        HostRoleCommand nnCommand = null;

        for (HostRoleCommand hrc : actionDB.getRequestTasks(response.getRequestId())) {
            if (hrc.getHostName().equals("h1")) {
                nnCommand = hrc;
            }
        }

        Assert.assertNotNull(nnCommand);
        ExecutionCommand cmd = nnCommand.getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertEquals("a1", cmd.getRole());
        Type type = new TypeToken<Map<String, String>>() {
        }.getType();
        for (Stage stage : actionDB.getAllStages(response.getRequestId())) {
            Map<String, String> commandParamsStage = StageUtils.getGson().fromJson(stage.getCommandParamsStage(),
                    type);
            Assert.assertTrue(commandParamsStage.containsKey("test"));
        }
    }

    @Test
    public void testResourceFiltersWithCustomCommands() throws AmbariException {
        setupClusterWithHosts("c1", "HDP-2.0.6", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
                add("h3");
            }
        }, "centos6");

        Cluster cluster = clusters.getCluster("c1");
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.6"));
        cluster.setCurrentStackVersion(new StackId("HDP-2.0.6"));

        ConfigFactory cf = injector.getInstance(ConfigFactory.class);
        Config config1 = cf.createNew(cluster, "global", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config1.setTag("version1");

        Config config2 = cf.createNew(cluster, "core-site", new HashMap<String, String>() {
            {
                put("key1", "value1");
            }
        }, new HashMap<String, Map<String, String>>());
        config2.setTag("version1");

        cluster.addConfig(config1);
        cluster.addConfig(config2);

        Service hdfs = cluster.addService("HDFS");
        hdfs.persist();

        Service mapred = cluster.addService("YARN");
        mapred.persist();

        hdfs.addServiceComponent(Role.HDFS_CLIENT.name()).persist();
        hdfs.addServiceComponent(Role.NAMENODE.name()).persist();
        hdfs.addServiceComponent(Role.DATANODE.name()).persist();

        mapred.addServiceComponent(Role.RESOURCEMANAGER.name()).persist();

        hdfs.getServiceComponent(Role.HDFS_CLIENT.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.NAMENODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h1").persist();
        hdfs.getServiceComponent(Role.DATANODE.name()).addServiceComponentHost("h2").persist();

        mapred.getServiceComponent(Role.RESOURCEMANAGER.name()).addServiceComponentHost("h2").persist();

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
            }
        };

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        // Test multiple restarts
        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", Role.DATANODE.name(),
                new ArrayList<String>() {
                    {
                        add("h1");
                        add("h2");
                    }
                });
        resourceFilters.add(resourceFilter);
        resourceFilter = new RequestResourceFilter("YARN", Role.RESOURCEMANAGER.name(), new ArrayList<String>() {
            {
                add("h2");
            }
        });
        resourceFilters.add(resourceFilter);

        ExecuteActionRequest request = new ExecuteActionRequest("c1", "RESTART", null, resourceFilters, null,
                params, false);

        RequestStatusResponse response = controller.createAction(request, requestProperties);
        Assert.assertEquals(3, response.getTasks().size());
        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());

        Assert.assertNotNull(storedTasks);
        int expectedRestartCount = 0;
        for (HostRoleCommand hrc : storedTasks) {
            Assert.assertEquals("RESTART", hrc.getCustomCommandName());

            if (hrc.getHostName().equals("h1") && hrc.getRole().equals(Role.DATANODE)) {
                expectedRestartCount++;
            } else if (hrc.getHostName().equals("h2")) {
                if (hrc.getRole().equals(Role.DATANODE)) {
                    expectedRestartCount++;
                } else if (hrc.getRole().equals(Role.RESOURCEMANAGER)) {
                    expectedRestartCount++;
                }
            }
        }

        Assert.assertEquals("Restart 2 datanodes and 1 Resourcemanager.", 3, expectedRestartCount);

        // Test service checks - specific host
        resourceFilters.clear();
        resourceFilter = new RequestResourceFilter("HDFS", null, new ArrayList<String>() {
            {
                add("h1");
            }
        });
        resourceFilters.add(resourceFilter);
        request = new ExecuteActionRequest("c1", Role.HDFS_SERVICE_CHECK.name(), null, resourceFilters, null, null,
                false);
        response = controller.createAction(request, requestProperties);

        Assert.assertEquals(1, response.getTasks().size());
        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Assert.assertNotNull(storedTasks);
        Assert.assertEquals(Role.HDFS_SERVICE_CHECK.name(), storedTasks.get(0).getRole().name());
        Assert.assertEquals("h1", storedTasks.get(0).getHostName());
    }

    @Test
    public void testConfigsAttachedToServiceChecks() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);

        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr2));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Install
        installService(clusterName, serviceName, false, false);
        // Start
        long requestId = startService(clusterName, serviceName, true, false);

        List<Stage> stages = actionDB.getAllStages(requestId);
        boolean serviceCheckFound = false;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HDFS_SERVICE_CHECK)) {
                    serviceCheckFound = true;
                    Assert.assertEquals(2,
                            hrc.getExecutionCommandWrapper().getExecutionCommand().getConfigurationTags().size());
                }
            }
        }

        Type type = new TypeToken<Map<String, String>>() {
        }.getType();
        for (Stage stage : actionDB.getAllStages(requestId)) {
            Map<String, String> hostParamsStage = StageUtils.getGson().fromJson(stage.getHostParamsStage(), type);
            Assert.assertNotNull(hostParamsStage.get("jdk_location"));
        }

        Assert.assertEquals(true, serviceCheckFound);
    }

    @Test
    @Ignore("Unsuported feature !")
    public void testConfigsAttachedToServiceNotCluster() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));

        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);

        // create, but don't assign
        controller.createConfiguration(cr1);
        controller.createConfiguration(cr2);

        Map<String, String> configVersions = new HashMap<String, String>() {
            {
                put("core-site", "version1");
                put("hdfs-site", "version1");
            }
        };
        ServiceRequest sr = new ServiceRequest(clusterName, serviceName, null);
        ServiceResourceProviderTest.updateServices(controller, Collections.singleton(sr),
                new HashMap<String, String>(), false, false);

        // Install
        installService(clusterName, serviceName, false, false);
        // Start
        long requestId = startService(clusterName, serviceName, true, false);

        Assert.assertEquals(0, clusters.getCluster(clusterName).getDesiredConfigs().size());

        List<Stage> stages = actionDB.getAllStages(requestId);
        boolean serviceCheckFound = false;
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HDFS_SERVICE_CHECK)) {
                    serviceCheckFound = true;
                    Assert.assertEquals(2,
                            hrc.getExecutionCommandWrapper().getExecutionCommand().getConfigurationTags().size());
                }
            }
        }
        Assert.assertEquals(true, serviceCheckFound);
    }

    @Test
    public void testHostLevelParamsSentWithCommands() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "PIG";
        createService(clusterName, serviceName, null);
        String componentName1 = "PIG";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, null, componentName1, host2, null);

        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        RequestStatusResponse trackAction = ServiceResourceProviderTest.updateServices(controller, requests,
                mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        List<Stage> stages = actionDB.getAllStages(trackAction.getRequestId());
        Type type = new TypeToken<Map<String, String>>() {
        }.getType();

        for (Stage stage : stages) {
            Map<String, String> params = StageUtils.getGson().fromJson(stage.getHostParamsStage(), type);
            Assert.assertEquals("0.1", params.get("stack_version"));
            Assert.assertNotNull(params.get("jdk_location"));
            Assert.assertNotNull(params.get("db_name"));
            Assert.assertNotNull(params.get("mysql_jdbc_url"));
            Assert.assertNotNull(params.get("oracle_jdbc_url"));
        }

        Map<String, String> paramsCmd = stages.get(0).getOrderedHostRoleCommands().get(0)
                .getExecutionCommandWrapper().getExecutionCommand().getHostLevelParams();
        Assert.assertNotNull(paramsCmd.get("repo_info"));
        Assert.assertNotNull(paramsCmd.get("clientsToUpdateConfigs"));
    }

    @Test
    public void testConfigGroupOverridesWithHostActions() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.6"));
        String serviceName1 = "HDFS";
        String serviceName2 = "MAPREDUCE2";
        createService(clusterName, serviceName1, null);
        createService(clusterName, serviceName2, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        String componentName4 = "HISTORYSERVER";

        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName3, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName4, State.INIT);

        String host1 = "h1";
        String host2 = "h2";
        String host3 = "h3";

        addHost(host1, clusterName);
        addHost(host2, clusterName);
        addHost(host3, clusterName);

        createServiceComponentHost(clusterName, serviceName1, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName1, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName1, componentName3, host2, null);
        createServiceComponentHost(clusterName, serviceName1, componentName3, host3, null);
        createServiceComponentHost(clusterName, serviceName2, componentName4, host3, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2, cr3;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);
        cr3 = new ConfigurationRequest(clusterName, "mapred-site", "version1", configs, null);

        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr2));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr3));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Create Config group for core-site
        configs = new HashMap<String, String>();
        configs.put("a", "c");
        cluster = clusters.getCluster(clusterName);
        final Config config = new ConfigImpl("core-site");
        config.setProperties(configs);
        config.setTag("version122");
        Long groupId = createConfigGroup(cluster, "g1", "t1", new ArrayList<String>() {
            {
                add("h1");
            }
        }, new ArrayList<Config>() {
            {
                add(config);
            }
        });

        Assert.assertNotNull(groupId);

        // Create Config group for mapred-site
        configs = new HashMap<String, String>();
        configs.put("a", "c");

        final Config config2 = new ConfigImpl("mapred-site");
        config2.setProperties(configs);
        config2.setTag("version122");
        groupId = createConfigGroup(cluster, "g2", "t2", new ArrayList<String>() {
            {
                add("h1");
            }
        }, new ArrayList<Config>() {
            {
                add(config2);
            }
        });

        Assert.assertNotNull(groupId);

        // Install
        Long requestId = installService(clusterName, serviceName1, false, false);
        HostRoleCommand namenodeInstall = null;
        HostRoleCommand clientInstall = null;
        HostRoleCommand slaveInstall = null;
        for (Stage stage : actionDB.getAllStages(requestId)) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.NAMENODE) && hrc.getHostName().equals("h1")) {
                    namenodeInstall = hrc;
                } else if (hrc.getRole().equals(Role.HDFS_CLIENT) && hrc.getHostName().equals("h3")) {
                    clientInstall = hrc;
                } else if (hrc.getRole().equals(Role.DATANODE) && hrc.getHostName().equals("h2")) {
                    slaveInstall = hrc;
                }
            }
        }

        Assert.assertNotNull(namenodeInstall);
        Assert.assertNotNull(clientInstall);
        Assert.assertNotNull(slaveInstall);
        Assert.assertTrue(namenodeInstall.getExecutionCommandWrapper().getExecutionCommand().getConfigurations()
                .get("core-site").containsKey("a"));
        Assert.assertEquals("c", namenodeInstall.getExecutionCommandWrapper().getExecutionCommand()
                .getConfigurations().get("core-site").get("a"));

        // Slave and client should not have the override
        Assert.assertTrue(clientInstall.getExecutionCommandWrapper().getExecutionCommand().getConfigurations()
                .get("core-site").containsKey("a"));
        Assert.assertEquals("b", clientInstall.getExecutionCommandWrapper().getExecutionCommand()
                .getConfigurations().get("core-site").get("a"));
        Assert.assertTrue(slaveInstall.getExecutionCommandWrapper().getExecutionCommand().getConfigurations()
                .get("core-site").containsKey("a"));
        Assert.assertEquals("b", slaveInstall.getExecutionCommandWrapper().getExecutionCommand().getConfigurations()
                .get("core-site").get("a"));

        startService(clusterName, serviceName1, false, false);

        requestId = installService(clusterName, serviceName2, false, false);
        HostRoleCommand mapredInstall = null;
        for (Stage stage : actionDB.getAllStages(requestId)) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HISTORYSERVER) && hrc.getHostName().equals("h3")) {
                    mapredInstall = hrc;
                }
            }
        }
        Assert.assertNotNull(mapredInstall);
        // Config group not associated with host
        Assert.assertEquals("b", mapredInstall.getExecutionCommandWrapper().getExecutionCommand()
                .getConfigurations().get("mapred-site").get("a"));

        // Associate the right host
        ConfigGroup configGroup = cluster.getConfigGroups().get(groupId);
        configGroup.setHosts(new HashMap<String, Host>() {
            {
                put("h3", clusters.getHost("h3"));
            }
        });
        configGroup.persist();

        requestId = startService(clusterName, serviceName2, false, false);
        mapredInstall = null;
        for (Stage stage : actionDB.getAllStages(requestId)) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HISTORYSERVER) && hrc.getHostName().equals("h3")) {
                    mapredInstall = hrc;
                }
            }
        }
        Assert.assertNotNull(mapredInstall);
        Assert.assertEquals("c", mapredInstall.getExecutionCommandWrapper().getExecutionCommand()
                .getConfigurations().get("mapred-site").get("a"));

    }

    @Test
    public void testConfigGroupOverridesWithDecommissionDatanode() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-2.0.7"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2;
        cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);
        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Start
        startService(clusterName, serviceName, false, false);

        // Create Config group for hdfs-site
        configs = new HashMap<String, String>();
        configs.put("a", "c");

        final Config config = new ConfigImpl("hdfs-site");
        config.setProperties(configs);
        config.setTag("version122");
        Long groupId = createConfigGroup(clusters.getCluster(clusterName), "g1", "t1", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
            }
        }, new ArrayList<Config>() {
            {
                add(config);
            }
        });

        Assert.assertNotNull(groupId);

        cluster = clusters.getCluster(clusterName);
        Service s = cluster.getService(serviceName);
        Assert.assertEquals(State.STARTED, s.getDesiredState());

        Map<String, String> params = new HashMap<String, String>() {
            {
                put("test", "test");
                put("excluded_hosts", " h1 ");
            }
        };
        RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", "NAMENODE", null);
        ExecuteActionRequest request = new ExecuteActionRequest(clusterName, "DECOMMISSION", params, false);
        request.getResourceFilters().add(resourceFilter);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        RequestStatusResponse response = controller.createAction(request, requestProperties);

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        ExecutionCommand execCmd = storedTasks.get(0).getExecutionCommandWrapper().getExecutionCommand();
        Assert.assertNotNull(storedTasks);
        Assert.assertNotNull(execCmd.getConfigurationTags().get("hdfs-site"));
        Assert.assertEquals(1, storedTasks.size());
        HostRoleCommand command = storedTasks.get(0);
        Assert.assertEquals(Role.NAMENODE, command.getRole());
        Assert.assertEquals(RoleCommand.CUSTOM_COMMAND, command.getRoleCommand());
        Assert.assertEquals("DECOMMISSION", execCmd.getHostLevelParams().get("custom_command"));
    }

    @Test
    public void testConfigGroupOverridesWithServiceCheckActions() throws AmbariException {
        String clusterName = "foo1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Create and attach config
        Map<String, String> configs = new HashMap<String, String>();
        configs.put("a", "b");

        ConfigurationRequest cr1, cr2;
        cr1 = new ConfigurationRequest(clusterName, "core-site", "version1", configs, null);
        cr2 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", configs, null);

        ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr1));
        controller.updateClusters(Collections.singleton(crReq), null);
        crReq = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq.setDesiredConfig(Collections.singletonList(cr2));
        controller.updateClusters(Collections.singleton(crReq), null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create Config group for hdfs-site
        configs = new HashMap<String, String>();
        configs.put("a", "c");

        final Config config = new ConfigImpl("hdfs-site");
        config.setProperties(configs);
        config.setTag("version122");
        Long groupId = createConfigGroup(clusters.getCluster(clusterName), "g1", "t1", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
            }
        }, new ArrayList<Config>() {
            {
                add(config);
            }
        });

        Assert.assertNotNull(groupId);

        // Start
        long requestId = startService(clusterName, serviceName, true, false);
        HostRoleCommand smokeTestCmd = null;
        List<Stage> stages = actionDB.getAllStages(requestId);
        for (Stage stage : stages) {
            for (HostRoleCommand hrc : stage.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HDFS_SERVICE_CHECK)) {
                    Assert.assertEquals(2,
                            hrc.getExecutionCommandWrapper().getExecutionCommand().getConfigurationTags().size());
                    smokeTestCmd = hrc;
                }
            }
        }
        Assert.assertNotNull(smokeTestCmd);
        Assert.assertEquals("c", smokeTestCmd.getExecutionCommandWrapper().getExecutionCommand().getConfigurations()
                .get("hdfs-site").get("a"));
    }

    @Test
    public void testGetStacks() throws Exception {

        StackRequest request = new StackRequest(null);
        Set<StackResponse> responses = controller.getStacks(Collections.singleton(request));
        Assert.assertEquals(STACKS_CNT, responses.size());

        StackRequest requestWithParams = new StackRequest(STACK_NAME);
        Set<StackResponse> responsesWithParams = controller.getStacks(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getStackName(), STACK_NAME);

        }

        StackRequest invalidRequest = new StackRequest(NON_EXT_VALUE);
        try {
            controller.getStacks(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testGetStackVersions() throws Exception {

        StackVersionRequest request = new StackVersionRequest(STACK_NAME, null);
        Set<StackVersionResponse> responses = controller.getStackVersions(Collections.singleton(request));
        Assert.assertEquals(STACK_VERSIONS_CNT, responses.size());

        StackVersionRequest requestWithParams = new StackVersionRequest(STACK_NAME, STACK_VERSION);
        Set<StackVersionResponse> responsesWithParams = controller
                .getStackVersions(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackVersionResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getStackVersion(), STACK_VERSION);
        }

        StackVersionRequest invalidRequest = new StackVersionRequest(STACK_NAME, NON_EXT_VALUE);
        try {
            controller.getStackVersions(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }

        // test that a stack response has upgrade packs
        requestWithParams = new StackVersionRequest(STACK_NAME, "2.1.1");
        responsesWithParams = controller.getStackVersions(Collections.singleton(requestWithParams));

        Assert.assertEquals(1, responsesWithParams.size());
        StackVersionResponse resp = responsesWithParams.iterator().next();
        assertNotNull(resp.getUpgradePacks());
        assertEquals(4, resp.getUpgradePacks().size());
        assertTrue(resp.getUpgradePacks().contains("upgrade_test"));
    }

    @Test
    public void testGetStackVersionActiveAttr() throws Exception {

        for (StackInfo stackInfo : ambariMetaInfo.getStacks(STACK_NAME)) {
            if (stackInfo.getVersion().equalsIgnoreCase(STACK_VERSION)) {
                stackInfo.setActive(true);
            }
        }

        StackVersionRequest requestWithParams = new StackVersionRequest(STACK_NAME, STACK_VERSION);
        Set<StackVersionResponse> responsesWithParams = controller
                .getStackVersions(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackVersionResponse responseWithParams : responsesWithParams) {
            Assert.assertTrue(responseWithParams.isActive());
        }
    }

    @Test
    public void testGetRepositories() throws Exception {

        RepositoryRequest request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, null);
        Set<RepositoryResponse> responses = controller.getRepositories(Collections.singleton(request));
        Assert.assertEquals(REPOS_CNT, responses.size());

        RepositoryRequest requestWithParams = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        Set<RepositoryResponse> responsesWithParams = controller
                .getRepositories(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (RepositoryResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getRepoId(), REPO_ID);

        }

        RepositoryRequest invalidRequest = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, NON_EXT_VALUE);
        try {
            controller.getRepositories(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testGetStackServices() throws Exception {
        StackServiceRequest request = new StackServiceRequest(STACK_NAME, NEW_STACK_VERSION, null);
        Set<StackServiceResponse> responses = controller.getStackServices(Collections.singleton(request));
        Assert.assertEquals(11, responses.size());

        StackServiceRequest requestWithParams = new StackServiceRequest(STACK_NAME, NEW_STACK_VERSION,
                SERVICE_NAME);
        Set<StackServiceResponse> responsesWithParams = controller
                .getStackServices(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackServiceResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getServiceName(), SERVICE_NAME);
            Assert.assertTrue(responseWithParams.getConfigTypes().size() > 0);
        }

        StackServiceRequest invalidRequest = new StackServiceRequest(STACK_NAME, NEW_STACK_VERSION, NON_EXT_VALUE);
        try {
            controller.getStackServices(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testConfigInComponent() throws Exception {
        StackServiceRequest requestWithParams = new StackServiceRequest(STACK_NAME, "2.0.6", "YARN");
        Set<StackServiceResponse> responsesWithParams = controller
                .getStackServices(Collections.singleton(requestWithParams));

        Assert.assertEquals(1, responsesWithParams.size());

        for (StackServiceResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getServiceName(), "YARN");
            Assert.assertTrue(responseWithParams.getConfigTypes().containsKey("capacity-scheduler"));
        }
    }

    @Test
    public void testGetStackConfigurations() throws Exception {
        StackConfigurationRequest request = new StackConfigurationRequest(STACK_NAME, STACK_VERSION, SERVICE_NAME,
                null);
        Set<StackConfigurationResponse> responses = controller
                .getStackConfigurations(Collections.singleton(request));
        Assert.assertEquals(STACK_PROPERTIES_CNT, responses.size());

        StackConfigurationRequest requestWithParams = new StackConfigurationRequest(STACK_NAME, STACK_VERSION,
                SERVICE_NAME, PROPERTY_NAME);
        Set<StackConfigurationResponse> responsesWithParams = controller
                .getStackConfigurations(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackConfigurationResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getPropertyName(), PROPERTY_NAME);

        }

        StackConfigurationRequest invalidRequest = new StackConfigurationRequest(STACK_NAME, STACK_VERSION,
                SERVICE_NAME, NON_EXT_VALUE);
        try {
            controller.getStackConfigurations(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testGetStackComponents() throws Exception {
        StackServiceComponentRequest request = new StackServiceComponentRequest(STACK_NAME, STACK_VERSION,
                SERVICE_NAME, null);
        Set<StackServiceComponentResponse> responses = controller
                .getStackComponents(Collections.singleton(request));
        Assert.assertEquals(STACK_COMPONENTS_CNT, responses.size());

        StackServiceComponentRequest requestWithParams = new StackServiceComponentRequest(STACK_NAME, STACK_VERSION,
                SERVICE_NAME, COMPONENT_NAME);
        Set<StackServiceComponentResponse> responsesWithParams = controller
                .getStackComponents(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (StackServiceComponentResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getComponentName(), COMPONENT_NAME);

        }

        StackServiceComponentRequest invalidRequest = new StackServiceComponentRequest(STACK_NAME, STACK_VERSION,
                SERVICE_NAME, NON_EXT_VALUE);
        try {
            controller.getStackComponents(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testGetStackOperatingSystems() throws Exception {
        OperatingSystemRequest request = new OperatingSystemRequest(STACK_NAME, STACK_VERSION, null);
        Set<OperatingSystemResponse> responses = controller.getOperatingSystems(Collections.singleton(request));
        Assert.assertEquals(OS_CNT, responses.size());

        OperatingSystemRequest requestWithParams = new OperatingSystemRequest(STACK_NAME, STACK_VERSION, OS_TYPE);
        Set<OperatingSystemResponse> responsesWithParams = controller
                .getOperatingSystems(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (OperatingSystemResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getOsType(), OS_TYPE);

        }

        OperatingSystemRequest invalidRequest = new OperatingSystemRequest(STACK_NAME, STACK_VERSION,
                NON_EXT_VALUE);
        try {
            controller.getOperatingSystems(Collections.singleton(invalidRequest));
        } catch (StackAccessException e) {
            // do nothing
        }
    }

    @Test
    public void testStackServiceCheckSupported() throws Exception {
        StackServiceRequest hdfsServiceRequest = new StackServiceRequest(STACK_NAME, "2.0.8", SERVICE_NAME);

        Set<StackServiceResponse> responses = controller
                .getStackServices(Collections.singleton(hdfsServiceRequest));
        Assert.assertEquals(1, responses.size());

        StackServiceResponse response = responses.iterator().next();
        assertTrue(response.isServiceCheckSupported());

        StackServiceRequest fakeServiceRequest = new StackServiceRequest(STACK_NAME, "2.0.8", FAKE_SERVICE_NAME);

        responses = controller.getStackServices(Collections.singleton(fakeServiceRequest));
        Assert.assertEquals(1, responses.size());

        response = responses.iterator().next();
        assertFalse(response.isServiceCheckSupported());
    }

    @Test
    public void testStackServiceComponentCustomCommands() throws Exception {
        StackServiceComponentRequest namenodeRequest = new StackServiceComponentRequest(STACK_NAME,
                NEW_STACK_VERSION, SERVICE_NAME, COMPONENT_NAME);

        Set<StackServiceComponentResponse> responses = controller
                .getStackComponents(Collections.singleton(namenodeRequest));
        Assert.assertEquals(1, responses.size());

        StackServiceComponentResponse response = responses.iterator().next();
        assertNotNull(response.getCustomCommands());
        assertEquals(2, response.getCustomCommands().size());
        assertEquals("DECOMMISSION", response.getCustomCommands().get(0));
        assertEquals("REBALANCEHDFS", response.getCustomCommands().get(1));

        StackServiceComponentRequest journalNodeRequest = new StackServiceComponentRequest(STACK_NAME,
                NEW_STACK_VERSION, SERVICE_NAME, "JOURNALNODE");

        responses = controller.getStackComponents(Collections.singleton(journalNodeRequest));
        Assert.assertEquals(1, responses.size());

        response = responses.iterator().next();
        assertNotNull(response.getCustomCommands());
        assertEquals(0, response.getCustomCommands().size());
    }

    // disabled as upgrade feature is disabled
    @Ignore
    @Test
    public void testUpdateClusterVersionBasic() throws AmbariException {
        String clusterName = "foo1";
        String serviceName = "MAPREDUCE";
        String host1 = "h1";
        String host2 = "h2";
        String componentName = "JOBTRACKER";
        StackId currentStackId = new StackId("HDP-0.1");

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createCluster(clusterName);
        Cluster c = clusters.getCluster(clusterName);
        c.setDesiredStackVersion(currentStackId);
        createService(clusterName, serviceName, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName, null);

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, null, componentName, host1, null);
        createServiceComponentHost(clusterName, null, componentName, host2, null);

        c.getService(serviceName).setDesiredState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).setDesiredState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host1)
                .setDesiredState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setDesiredState(State.STARTED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host1)
                .setState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setState(State.STARTED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host1)
                .setStackVersion(currentStackId);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setStackVersion(currentStackId);

        ClusterRequest r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.0.1", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
            fail("Update cluster should fail");
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("must be greater than current version"));
        }

        r = new ClusterRequest(c.getClusterId(), clusterName, "HDPLocal-1.2.2", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
            fail("Update cluster should fail");
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("Upgrade not possible between different stacks"));
        }

        r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
            fail("Update cluster should fail");
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("Upgrade needs all services to be stopped"));
            Assert.assertTrue(e.getMessage().contains(serviceName));
        }

        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setDesiredState(State.INSTALLED);

        r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
            fail("Update cluster should fail");
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("Upgrade needs all services to be stopped"));
            Assert.assertTrue(e.getMessage().contains(componentName));
        }

        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setState(State.INSTALLED);
        controller.updateClusters(Collections.singleton(r), mapRequestProps);
        StackId expectedStackId = new StackId("HDP-0.2");
        Assert.assertTrue(expectedStackId.equals(c.getDesiredStackVersion()));
        Assert.assertTrue(expectedStackId.equals(c.getService(serviceName).getDesiredStackVersion()));
        Assert.assertTrue(expectedStackId
                .equals(c.getService(serviceName).getServiceComponent(componentName).getDesiredStackVersion()));
        Assert.assertTrue(expectedStackId.equals(c.getService(serviceName).getServiceComponent(componentName)
                .getServiceComponentHost(host1).getDesiredStackVersion()));
        Assert.assertTrue(expectedStackId.equals(c.getService(serviceName).getServiceComponent(componentName)
                .getServiceComponentHost(host2).getDesiredStackVersion()));
        Assert.assertTrue(currentStackId.equals(c.getService(serviceName).getServiceComponent(componentName)
                .getServiceComponentHost(host1).getStackVersion()));
        Assert.assertTrue(currentStackId.equals(c.getService(serviceName).getServiceComponent(componentName)
                .getServiceComponentHost(host2).getStackVersion()));
        ServiceComponent sc = c.getService(serviceName).getServiceComponent(componentName);
        Assert.assertEquals(State.UPGRADING, sc.getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.UPGRADING, sc.getServiceComponentHost(host2).getState());

        // Fail as another request is active
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
            fail("Update cluster should fail");
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("A prior upgrade request with id"));
        }

        // cases where there is no update required
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host1)
                .setDesiredState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setDesiredState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host1)
                .setState(State.INSTALLED);
        c.getService(serviceName).getServiceComponent(componentName).getServiceComponentHost(host2)
                .setState(State.INSTALLED);
        c.setCurrentStackVersion(expectedStackId);
        r = new ClusterRequest(c.getClusterId(), clusterName, "", null);
        controller.updateClusters(Collections.singleton(r), mapRequestProps);
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host2).getState());

        r = new ClusterRequest(c.getClusterId(), clusterName, null, null);
        controller.updateClusters(Collections.singleton(r), mapRequestProps);
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host2).getState());

        r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        controller.updateClusters(Collections.singleton(r), mapRequestProps);
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host1).getState());
        Assert.assertEquals(State.INSTALLED, sc.getServiceComponentHost(host2).getState());
    }

    @Test
    public void testUpdateClusterUpgradabilityCheck() throws AmbariException {
        String clusterName = "foo1";
        StackId currentStackId = new StackId("HDP-0.2");

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createCluster(clusterName);
        Cluster c = clusters.getCluster(clusterName);
        c.setDesiredStackVersion(currentStackId);
        ClusterRequest r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.3", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("Illegal request to upgrade to"));
        }

        StackId unsupportedStackId = new StackId("HDP-0.0.1");
        c.setDesiredStackVersion(unsupportedStackId);
        c.setCurrentStackVersion(unsupportedStackId);
        c.refresh();
        r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        try {
            controller.updateClusters(Collections.singleton(r), mapRequestProps);
        } catch (AmbariException e) {
            Assert.assertTrue(e.getMessage().contains("Upgrade is not allowed from"));
        }
    }

    // disabled as cluster upgrade feature is disabled
    @Ignore
    @Test
    public void testUpdateClusterVersionCombinations() throws AmbariException {
        String clusterName = "foo1";
        String pigServiceName = "PIG";
        String mrServiceName = "MAPREDUCE";
        String host1 = "h1";
        String host2 = "h2";
        String pigComponentName = "PIG";
        String mrJobTrackerComp = "JOBTRACKER";
        String mrTaskTrackerComp = "TASKTRACKER";
        String mrClientComp = "MAPREDUCE_CLIENT";
        String hdfsService = "HDFS";
        String hdfsNameNode = "NAMENODE";
        String hdfsDataNode = "DATANODE";
        String hdfsClient = "HDFS_CLIENT";
        StackId currentStackId = new StackId("HDP-0.1");
        StackId desiredStackId = new StackId("HDP-0.2");

        List<String> hosts = new ArrayList<String>();
        hosts.add(host1);
        hosts.add(host2);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        createCluster(clusterName);
        Cluster c = clusters.getCluster(clusterName);
        c.setDesiredStackVersion(currentStackId);
        createService(clusterName, pigServiceName, State.INIT);
        createServiceComponent(clusterName, pigServiceName, pigComponentName, null);

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, null, pigComponentName, host1, null);
        createServiceComponentHost(clusterName, null, pigComponentName, host2, null);

        resetServiceState(pigServiceName, currentStackId, c);

        ClusterRequest r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        RequestStatusResponse trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        List<Stage> stages = actionDB.getAllStages(trackAction.getRequestId());

        // Upgrade a cluster with one service
        ExpectedUpgradeTasks expectedTasks = new ExpectedUpgradeTasks(hosts);
        expectedTasks.expectTask(Role.PIG, host1);
        expectedTasks.expectTask(Role.PIG, host2);
        expectedTasks.expectTask(Role.AMBARI_SERVER_ACTION);
        validateGeneratedStages(stages, 2, expectedTasks);

        resetCluster(c, currentStackId);

        createService(clusterName, mrServiceName, State.INIT);
        createServiceComponent(clusterName, mrServiceName, mrJobTrackerComp, null);
        createServiceComponent(clusterName, mrServiceName, mrTaskTrackerComp, null);
        createServiceComponent(clusterName, mrServiceName, mrClientComp, null);

        createServiceComponentHost(clusterName, null, mrJobTrackerComp, host1, null);
        createServiceComponentHost(clusterName, null, mrTaskTrackerComp, host2, null);
        createServiceComponentHost(clusterName, null, mrClientComp, host2, null);

        resetServiceState(mrServiceName, currentStackId, c);

        // Upgrade a cluster with two service
        actionDB.abortOperation(trackAction.getRequestId());
        r = new ClusterRequest(c.getClusterId(), clusterName, "HDP-0.2", null);
        trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        stages = actionDB.getAllStages(trackAction.getRequestId());

        expectedTasks.expectTask(Role.JOBTRACKER, host1);
        expectedTasks.expectTask(Role.TASKTRACKER, host2);
        expectedTasks.expectTask(Role.MAPREDUCE_CLIENT, host2);
        validateGeneratedStages(stages, 5, expectedTasks);

        // Upgrade again
        actionDB.abortOperation(trackAction.getRequestId());
        trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        stages = actionDB.getAllStages(trackAction.getRequestId());
        validateGeneratedStages(stages, 5, expectedTasks);

        // some host components are upgraded
        c.getService(pigServiceName).getServiceComponent(pigComponentName).getServiceComponentHost(host1)
                .setState(State.INSTALLED);
        c.getService(pigServiceName).getServiceComponent(pigComponentName).getServiceComponentHost(host2)
                .setState(State.INSTALLED);
        c.getService(pigServiceName).getServiceComponent(pigComponentName).getServiceComponentHost(host1)
                .setStackVersion(desiredStackId);
        c.getService(pigServiceName).getServiceComponent(pigComponentName).getServiceComponentHost(host2)
                .setStackVersion(desiredStackId);

        actionDB.abortOperation(trackAction.getRequestId());
        trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        stages = actionDB.getAllStages(trackAction.getRequestId());
        validateGeneratedStages(stages, 5, expectedTasks);

        c.getService(mrServiceName).getServiceComponent(mrJobTrackerComp).getServiceComponentHost(host1)
                .setState(State.UPGRADING);
        c.getService(mrServiceName).getServiceComponent(mrTaskTrackerComp).getServiceComponentHost(host2)
                .setState(State.UPGRADING);
        actionDB.abortOperation(trackAction.getRequestId());
        trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        stages = actionDB.getAllStages(trackAction.getRequestId());
        validateGeneratedStages(stages, 5, expectedTasks);

        // Add HDFS and upgrade
        createService(clusterName, hdfsService, State.INIT);
        createServiceComponent(clusterName, hdfsService, hdfsNameNode, null);
        createServiceComponent(clusterName, hdfsService, hdfsDataNode, null);
        createServiceComponent(clusterName, hdfsService, hdfsClient, null);

        createServiceComponentHost(clusterName, null, hdfsNameNode, host1, null);
        createServiceComponentHost(clusterName, null, hdfsDataNode, host1, null);
        createServiceComponentHost(clusterName, null, hdfsDataNode, host2, null);
        createServiceComponentHost(clusterName, null, hdfsClient, host2, null);

        resetServiceState(hdfsService, currentStackId, c);
        resetServiceState(mrServiceName, currentStackId, c);
        resetServiceState(pigServiceName, currentStackId, c);

        actionDB.abortOperation(trackAction.getRequestId());
        trackAction = controller.updateClusters(Collections.singleton(r), mapRequestProps);
        stages = actionDB.getAllStages(trackAction.getRequestId());

        expectedTasks.resetAll();
        expectedTasks.expectTask(Role.PIG, host1);
        expectedTasks.expectTask(Role.PIG, host2);
        expectedTasks.expectTask(Role.JOBTRACKER, host1);
        expectedTasks.expectTask(Role.TASKTRACKER, host2);
        expectedTasks.expectTask(Role.MAPREDUCE_CLIENT, host2);
        expectedTasks.expectTask(Role.DATANODE, host1);
        expectedTasks.expectTask(Role.DATANODE, host2);
        expectedTasks.expectTask(Role.NAMENODE, host1);
        expectedTasks.expectTask(Role.HDFS_CLIENT, host2);
        expectedTasks.expectTask(Role.AMBARI_SERVER_ACTION);
        validateGeneratedStages(stages, 8, expectedTasks);
    }

    private void resetServiceState(String service, StackId currentStackId, Cluster c) throws AmbariException {
        c.getService(service).setDesiredState(State.INSTALLED);
        for (ServiceComponent sc : c.getService(service).getServiceComponents().values()) {
            sc.setDesiredState(State.INSTALLED);
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setDesiredState(State.INSTALLED);
                sch.setState(State.INSTALLED);
                sch.setStackVersion(currentStackId);
            }
        }
    }

    private void validateGeneratedStages(List<Stage> stages, int expectedStageCount,
            ExpectedUpgradeTasks expectedTasks) {
        Assert.assertEquals(expectedStageCount, stages.size());
        int prevRoleOrder = -1;
        for (Stage stage : stages) {
            int currRoleOrder = -1;
            for (HostRoleCommand command : stage.getOrderedHostRoleCommands()) {
                if (command.getRole() == Role.AMBARI_SERVER_ACTION) {
                    Assert.assertTrue(command.toString(), expectedTasks.isTaskExpected(command.getRole()));
                    currRoleOrder = expectedTasks.getRoleOrder(command.getRole());
                    ExecutionCommand execCommand = command.getExecutionCommandWrapper().getExecutionCommand();
                    Assert.assertTrue(execCommand.getRoleParams().containsKey(ServerAction.ACTION_NAME));
                    Assert.assertEquals(RoleCommand.EXECUTE, execCommand.getRoleCommand());
                } else {
                    Assert.assertTrue(command.toString(),
                            expectedTasks.isTaskExpected(command.getRole(), command.getHostName()));
                    currRoleOrder = expectedTasks.getRoleOrder(command.getRole());
                    ExecutionCommand execCommand = command.getExecutionCommandWrapper().getExecutionCommand();
                    Assert.assertTrue(execCommand.getCommandParams().containsKey("source_stack_version"));
                    Assert.assertTrue(execCommand.getCommandParams().containsKey("target_stack_version"));
                    Assert.assertEquals(RoleCommand.UPGRADE, execCommand.getRoleCommand());
                }
            }

            List<HostRoleCommand> commands = stage.getOrderedHostRoleCommands();
            Assert.assertTrue(commands.size() > 0);
            Role role = commands.get(0).getRole();
            for (HostRoleCommand command : commands) {
                Assert.assertTrue("All commands must be for the same role", role.equals(command.getRole()));
            }

            Assert.assertTrue("Roles must be in order", currRoleOrder > prevRoleOrder);
            prevRoleOrder = currRoleOrder;
        }
    }

    private void resetCluster(Cluster cluster, StackId currentStackId) throws AmbariException {
        cluster.setDesiredStackVersion(currentStackId);
        for (Service service : cluster.getServices().values()) {
            service.setDesiredStackVersion(currentStackId);
            for (ServiceComponent component : service.getServiceComponents().values()) {
                component.setDesiredStackVersion(currentStackId);
                for (ServiceComponentHost componentHost : component.getServiceComponentHosts().values()) {
                    componentHost.setDesiredStackVersion(currentStackId);
                    componentHost.setState(State.INSTALLED);
                }
            }
        }
    }

    class ExpectedUpgradeTasks {
        private static final int ROLE_COUNT = 25;
        private static final String DEFAULT_HOST = "default_host";
        private ArrayList<Map<String, Boolean>> expectedList;
        private Map<Role, Integer> roleToIndex;

        public ExpectedUpgradeTasks(List<String> hosts) {
            roleToIndex = new HashMap<Role, Integer>();
            expectedList = new ArrayList<Map<String, Boolean>>(ROLE_COUNT);

            fillRoleToIndex();
            fillExpectedHosts(hosts);
        }

        public void expectTask(Role role, String host) {
            expectedList.get(roleToIndex.get(role)).put(host, true);
        }

        public void expectTask(Role role) {
            Assert.assertEquals(Role.AMBARI_SERVER_ACTION, role);
            expectTask(role, DEFAULT_HOST);
        }

        public boolean isTaskExpected(Role role, String host) {
            return expectedList.get(roleToIndex.get(role)).get(host);
        }

        public boolean isTaskExpected(Role role) {
            Assert.assertEquals(Role.AMBARI_SERVER_ACTION, role);
            return isTaskExpected(role, DEFAULT_HOST);
        }

        public int getRoleOrder(Role role) {
            return roleToIndex.get(role);
        }

        public void resetAll() {
            for (Role role : roleToIndex.keySet()) {
                Map<String, Boolean> hostState = expectedList.get(roleToIndex.get(role));
                for (String host : hostState.keySet()) {
                    hostState.put(host, false);
                }
            }
        }

        private void fillExpectedHosts(List<String> hosts) {
            for (int index = 0; index < ROLE_COUNT; index++) {
                Map<String, Boolean> hostState = new HashMap<String, Boolean>();
                for (String host : hosts) {
                    hostState.put(host, false);
                }
                expectedList.add(hostState);
            }
        }

        private void fillRoleToIndex() {
            roleToIndex.put(Role.NAMENODE, 0);
            roleToIndex.put(Role.SECONDARY_NAMENODE, 1);
            roleToIndex.put(Role.DATANODE, 2);
            roleToIndex.put(Role.HDFS_CLIENT, 3);
            roleToIndex.put(Role.JOBTRACKER, 4);
            roleToIndex.put(Role.TASKTRACKER, 5);
            roleToIndex.put(Role.MAPREDUCE_CLIENT, 6);
            roleToIndex.put(Role.ZOOKEEPER_SERVER, 7);
            roleToIndex.put(Role.ZOOKEEPER_CLIENT, 8);
            roleToIndex.put(Role.HBASE_MASTER, 9);

            roleToIndex.put(Role.HBASE_REGIONSERVER, 10);
            roleToIndex.put(Role.HBASE_CLIENT, 11);
            roleToIndex.put(Role.HIVE_SERVER, 12);
            roleToIndex.put(Role.HIVE_METASTORE, 13);
            roleToIndex.put(Role.HIVE_CLIENT, 14);
            roleToIndex.put(Role.HCAT, 15);
            roleToIndex.put(Role.OOZIE_SERVER, 16);
            roleToIndex.put(Role.OOZIE_CLIENT, 17);
            roleToIndex.put(Role.WEBHCAT_SERVER, 18);
            roleToIndex.put(Role.PIG, 19);

            roleToIndex.put(Role.SQOOP, 20);
            roleToIndex.put(Role.GANGLIA_SERVER, 21);
            roleToIndex.put(Role.GANGLIA_MONITOR, 22);
            roleToIndex.put(Role.AMBARI_SERVER_ACTION, 23);
        }
    }

    @Test
    public void testServiceStopWhileStopping() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host2));

        // Install
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // manually change live state to installed as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                sch.setState(State.INSTALLED);
            }
        }

        // Start
        r = new ServiceRequest(clusterName, serviceName, State.STARTED.toString());
        requests.clear();
        requests.add(r);
        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);

        // manually change live state to started as no running action manager
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (!sch.getServiceComponentName().equals("HDFS_CLIENT")) {
                    sch.setState(State.STARTED);
                }
            }
        }

        Assert.assertEquals(State.STARTED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // Set Current state to stopping
        clusters.getCluster(clusterName).getService(serviceName).setDesiredState(State.STOPPING);
        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {

            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (!sch.getServiceComponentName().equals("HDFS_CLIENT")) {
                    Assert.assertEquals(State.STARTED, sch.getDesiredState());
                    sch.setState(State.STOPPING);
                } else if (sch.getServiceComponentName().equals("DATANODE")) {
                    ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName,
                            sch.getServiceComponentName(), sch.getHostName(), State.INSTALLED.name());
                    Set<ServiceComponentHostRequest> reqs1 = new HashSet<ServiceComponentHostRequest>();
                    reqs1.add(r1);
                    updateHostComponents(reqs1, Collections.<String, String>emptyMap(), true);
                    Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                }
            }
        }

        // Stop all services
        r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        requests.clear();
        requests.add(r);
        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);

        for (ServiceComponent sc : clusters.getCluster(clusterName).getService(serviceName).getServiceComponents()
                .values()) {

            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                if (!sch.getServiceComponentName().equals("HDFS_CLIENT")) {
                    Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
                }
            }
        }
    }

    @Test
    public void testGetTasksByRequestId() throws AmbariException {
        final long requestId1 = 1;
        final long requestId2 = 2;
        final String clusterName = "c1";
        final String hostName1 = "h1";
        final String context = "Test invocation";

        clusters.addCluster(clusterName);
        Cluster c = clusters.getCluster(clusterName);
        StackId stackID = new StackId("HDP-0.1");
        c.setDesiredStackVersion(stackID);
        helper.getOrCreateRepositoryVersion(stackID.getStackName(), stackID.getStackVersion());
        c.createClusterVersion(stackID.getStackName(), stackID.getStackVersion(), "admin",
                RepositoryVersionState.UPGRADING);
        clusters.addHost(hostName1);
        setOsFamily(clusters.getHost("h1"), "redhat", "5.9");
        clusters.getHost(hostName1).persist();

        clusters.mapHostsToCluster(new HashSet<String>() {
            {
                add(hostName1);
            }
        }, clusterName);

        List<Stage> stages = new ArrayList<Stage>();
        stages.add(new Stage(requestId1, "/a1", clusterName, 1L, context, CLUSTER_HOST_INFO, "", ""));
        stages.get(0).setStageId(1);
        stages.get(0).addHostRoleExecutionCommand(hostName1, Role.HBASE_MASTER, RoleCommand.START,
                new ServiceComponentHostStartEvent(Role.HBASE_MASTER.toString(), hostName1,
                        System.currentTimeMillis()),
                clusterName, "HBASE", false);

        stages.add(new Stage(requestId1, "/a2", clusterName, 1L, context, CLUSTER_HOST_INFO, "", ""));
        stages.get(1).setStageId(2);
        stages.get(1).addHostRoleExecutionCommand(hostName1, Role.HBASE_CLIENT, RoleCommand.START,
                new ServiceComponentHostStartEvent(Role.HBASE_CLIENT.toString(), hostName1,
                        System.currentTimeMillis()),
                clusterName, "HBASE", false);

        stages.add(new Stage(requestId1, "/a3", clusterName, 1L, context, CLUSTER_HOST_INFO, "", ""));
        stages.get(2).setStageId(3);
        stages.get(2).addHostRoleExecutionCommand(hostName1, Role.HBASE_CLIENT, RoleCommand.START,
                new ServiceComponentHostStartEvent(Role.HBASE_CLIENT.toString(), hostName1,
                        System.currentTimeMillis()),
                clusterName, "HBASE", false);

        Request request = new Request(stages, clusters);
        actionDB.persistActions(request);

        stages.clear();
        stages.add(new Stage(requestId2, "/a4", clusterName, 1L, context, CLUSTER_HOST_INFO, "", ""));
        stages.get(0).setStageId(4);
        stages.get(0).addHostRoleExecutionCommand(hostName1, Role.HBASE_CLIENT, RoleCommand.START,
                new ServiceComponentHostStartEvent(Role.HBASE_CLIENT.toString(), hostName1,
                        System.currentTimeMillis()),
                clusterName, "HBASE", false);

        stages.add(new Stage(requestId2, "/a5", clusterName, 1L, context, CLUSTER_HOST_INFO, "", ""));
        stages.get(1).setStageId(5);
        stages.get(1).addHostRoleExecutionCommand(hostName1, Role.HBASE_CLIENT, RoleCommand.START,
                new ServiceComponentHostStartEvent(Role.HBASE_CLIENT.toString(), hostName1,
                        System.currentTimeMillis()),
                clusterName, "HBASE", false);

        request = new Request(stages, clusters);
        actionDB.persistActions(request);

        Set<TaskStatusRequest> taskStatusRequests;
        Set<TaskStatusResponse> taskStatusResponses;

        //check count of tasks by requestId1
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId1, null));
            }
        };
        taskStatusResponses = controller.getTaskStatus(taskStatusRequests);
        assertEquals(3, taskStatusResponses.size());

        //check a taskId that requested by requestId1 and task id
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId1, 2L));
            }
        };
        taskStatusResponses = controller.getTaskStatus(taskStatusRequests);
        assertEquals(1, taskStatusResponses.size());
        assertEquals(2L, taskStatusResponses.iterator().next().getTaskId());

        //check count of tasks by requestId2
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId2, null));
            }
        };
        taskStatusResponses = controller.getTaskStatus(taskStatusRequests);
        assertEquals(2, taskStatusResponses.size());

        //check a taskId that requested by requestId2 and task id
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId2, 5L));
            }
        };
        taskStatusResponses = controller.getTaskStatus(taskStatusRequests);
        assertEquals(5L, taskStatusResponses.iterator().next().getTaskId());

        //verify that task from second request (requestId2) does not present in first request (requestId1)
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId1, 5L));
            }
        };
        expectedException.expect(ObjectNotFoundException.class);
        expectedException.expectMessage("Task resource doesn't exist.");
        controller.getTaskStatus(taskStatusRequests);

        //verify that task from first request (requestId1) does not present in second request (requestId2)
        taskStatusRequests = new HashSet<TaskStatusRequest>() {
            {
                add(new TaskStatusRequest(requestId2, 2L));
            }
        };
        expectedException.expect(ObjectNotFoundException.class);
        expectedException.expectMessage("Task resource doesn't exist.");
        controller.getTaskStatus(taskStatusRequests);
    }

    @Test
    public void testUpdateHostComponentsBadState() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        Map<String, String> mapRequestProps = new HashMap<String, String>();
        mapRequestProps.put("context", "Called from a test");

        // null service should work
        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName2).getServiceComponentHost(host2));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host1));
        Assert.assertNotNull(clusters.getCluster(clusterName).getService(serviceName)
                .getServiceComponent(componentName3).getServiceComponentHost(host2));

        // Install
        ServiceRequest r = new ServiceRequest(clusterName, serviceName, State.INSTALLED.toString());
        Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
        requests.add(r);

        ServiceResourceProviderTest.updateServices(controller, requests, mapRequestProps, true, false);
        Assert.assertEquals(State.INSTALLED,
                clusters.getCluster(clusterName).getService(serviceName).getDesiredState());

        // set host components on host1 to INSTALLED
        for (ServiceComponentHost sch : clusters.getCluster(clusterName).getServiceComponentHosts(host1)) {
            sch.setState(State.INSTALLED);
        }

        // set the host components on host2 to UNKNOWN state to simulate a lost host
        for (ServiceComponentHost sch : clusters.getCluster(clusterName).getServiceComponentHosts(host2)) {
            sch.setState(State.UNKNOWN);
        }

        // issue an installed state request without failure
        ServiceComponentHostRequest schr = new ServiceComponentHostRequest(clusterName, "HDFS", "DATANODE", host2,
                "INSTALLED");
        Map<String, String> requestProps = new HashMap<String, String>();
        requestProps.put("datanode", "dn_value");
        requestProps.put("namenode", "nn_value");
        RequestStatusResponse rsr = updateHostComponents(Collections.singleton(schr), requestProps, false);

        List<Stage> stages = actionDB.getAllStages(rsr.getRequestId());
        Assert.assertEquals(1, stages.size());
        Stage stage = stages.iterator().next();
        List<ExecutionCommandWrapper> execWrappers = stage.getExecutionCommands(host2);
        Assert.assertEquals(1, execWrappers.size());
        ExecutionCommandWrapper execWrapper = execWrappers.iterator().next();
        Assert.assertTrue(execWrapper.getExecutionCommand().getCommandParams().containsKey("datanode"));
        Assert.assertFalse(execWrapper.getExecutionCommand().getCommandParams().containsKey("namendode"));

        // set the host components on host2 to UNKNOWN state to simulate a lost host
        for (ServiceComponentHost sch : clusters.getCluster(clusterName).getServiceComponentHosts(host2)) {
            Assert.assertEquals(State.UNKNOWN, sch.getState());
        }
    }

    @Test
    public void testServiceUpdateRecursiveBadHostComponent() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.2"));

        String serviceName1 = "HDFS";
        createService(clusterName, serviceName1, null);

        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName1, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName3, State.INIT);
        String host1 = "h1";
        addHost(host1, clusterName);

        Set<ServiceComponentHostRequest> set1 = new HashSet<ServiceComponentHostRequest>();
        ServiceComponentHostRequest r1 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName1,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r2 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName2,
                host1, State.INIT.toString());
        ServiceComponentHostRequest r3 = new ServiceComponentHostRequest(clusterName, serviceName1, componentName3,
                host1, State.INIT.toString());

        set1.add(r1);
        set1.add(r2);
        set1.add(r3);
        controller.createHostComponents(set1);

        Cluster c1 = clusters.getCluster(clusterName);
        Service s1 = c1.getService(serviceName1);

        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        ServiceComponent sc3 = s1.getServiceComponent(componentName3);
        ServiceComponentHost sch1 = sc1.getServiceComponentHost(host1);
        ServiceComponentHost sch2 = sc2.getServiceComponentHost(host1);
        ServiceComponentHost sch3 = sc3.getServiceComponentHost(host1);

        s1.setDesiredState(State.INSTALLED);
        sc1.setDesiredState(State.STARTED);
        sc2.setDesiredState(State.INIT);
        sc3.setDesiredState(State.INSTALLED);
        sch1.setDesiredState(State.INSTALLED);
        sch2.setDesiredState(State.INSTALLED);
        sch3.setDesiredState(State.INSTALLED);
        sch1.setState(State.INSTALLED);
        sch2.setState(State.UNKNOWN);
        sch3.setState(State.INSTALLED);

        // an UNKOWN failure will throw an exception
        ServiceRequest req = new ServiceRequest(clusterName, serviceName1, State.INSTALLED.toString());
        ServiceResourceProviderTest.updateServices(controller, Collections.singleton(req),
                Collections.<String, String>emptyMap(), true, false);
    }

    @Test
    public void testUpdateStacks() throws Exception {

        StackInfo stackInfo = ambariMetaInfo.getStack(STACK_NAME, STACK_VERSION);

        for (RepositoryInfo repositoryInfo : stackInfo.getRepositories()) {
            assertFalse(INCORRECT_BASE_URL.equals(repositoryInfo.getBaseUrl()));
            repositoryInfo.setBaseUrl(INCORRECT_BASE_URL);
            assertTrue(INCORRECT_BASE_URL.equals(repositoryInfo.getBaseUrl()));
        }

        controller.updateStacks();

        stackInfo = ambariMetaInfo.getStack(STACK_NAME, STACK_VERSION);

        for (RepositoryInfo repositoryInfo : stackInfo.getRepositories()) {
            assertFalse(INCORRECT_BASE_URL.equals(repositoryInfo.getBaseUrl()));
        }
    }

    @Test
    public void testUpdateRepoUrl() throws Exception {
        String INCORRECT_URL_2 = "http://bar.com/foo";

        RepositoryInfo repo = ambariMetaInfo.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertNotNull(repo);
        assertNotNull(repo.getBaseUrl());

        String original = repo.getBaseUrl();

        repo = ambariMetaInfo.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertEquals(original, repo.getBaseUrl());

        ambariMetaInfo.updateRepoBaseURL(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID, INCORRECT_BASE_URL);

        repo = ambariMetaInfo.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertEquals(INCORRECT_BASE_URL, repo.getBaseUrl());
        assertEquals(original, repo.getDefaultBaseUrl());

        ambariMetaInfo.updateRepoBaseURL(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID, INCORRECT_URL_2);
        repo = ambariMetaInfo.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertFalse(INCORRECT_BASE_URL.equals(repo.getBaseUrl()));
        assertEquals(INCORRECT_URL_2, repo.getBaseUrl());
        assertEquals(original, repo.getDefaultBaseUrl());

        // verify change with new meta info
        AmbariMetaInfo ami = new AmbariMetaInfo(new File("src/test/resources/stacks"), null,
                new File("src/test/resources/version"));
        injector.injectMembers(ami);
        ami.init();

        repo = ami.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertEquals(INCORRECT_URL_2, repo.getBaseUrl());
        assertNotNull(repo.getDefaultBaseUrl());
        assertEquals(original, repo.getDefaultBaseUrl());

        ami.updateRepoBaseURL(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID, original);
        repo = ami.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        assertEquals(original, repo.getBaseUrl());
        assertEquals(original, repo.getDefaultBaseUrl());
    }

    @Test
    public void testUpdateRepoUrlController() throws Exception {
        String badUrl = "http://hortonworks.com";
        RepositoryInfo repo = ambariMetaInfo.getRepository(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        RepositoryRequest request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        request.setBaseUrl(badUrl);

        Set<RepositoryRequest> requests = new HashSet<RepositoryRequest>();
        requests.add(request);

        // test bad url
        try {
            controller.updateRepositories(requests);
            Assert.fail("Expected a bad URL to throw an exception");
        } catch (Exception e) {
            assertNotNull(e);
            Assert.assertTrue(e.getMessage().contains(badUrl));
        }
        // test bad url, but allow to set anyway
        request.setVerifyBaseUrl(false);
        controller.updateRepositories(requests);
        Assert.assertEquals(request.getBaseUrl(), repo.getBaseUrl());

        requests.clear();
        request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        request.setBaseUrl("https://hortonworks.com");
        requests.add(request);
        // test bad url
        try {
            controller.updateRepositories(requests);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            assertTrue(e.getMessage().contains(IOException.class.getName())
                    || e.getMessage().contains("Could not access base url"));
        }

        requests.clear();
        request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        request.setBaseUrl("pro://hortonworks.com");
        requests.add(request);
        // test bad url
        try {
            controller.updateRepositories(requests);
        } catch (Exception e) {
            assertTrue(e.getMessage().contains(MalformedURLException.class.getName()));
        }

        requests.clear();
        request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        request.setBaseUrl("http://rrr1.cccc");
        requests.add(request);
        // test bad url
        try {
            controller.updateRepositories(requests);
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains(UnknownHostException.class.getName())
                    || exceptionMsg.contains(ConnectException.class.getName()));
        }

        // reset repo
        requests.clear();
        request = new RepositoryRequest(STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
        request.setBaseUrl(repo.getDefaultBaseUrl());
        requests.add(request);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(repo.getBaseUrl(), repo.getDefaultBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

        String baseUrl = repo.getDefaultBaseUrl();
        if (!baseUrl.endsWith("/")) {
            baseUrl += "/";
        }

        // variation #1: url with trailing slash, suffix preceding slash
        backingProperties.setProperty(Configuration.REPO_SUFFIX_KEY_UBUNTU, "/repodata/repomd.xml");
        Assert.assertTrue(
                baseUrl.endsWith("/") && configuration.getRepoValidationSuffixes("ubuntu12")[0].startsWith("/"));
        request.setBaseUrl(baseUrl);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(baseUrl, repo.getBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

        // variation #2: url with trailing slash, suffix no preceding slash
        backingProperties.setProperty(Configuration.REPO_SUFFIX_KEY_DEFAULT, "repodata/repomd.xml");
        Assert.assertTrue(
                baseUrl.endsWith("/") && !configuration.getRepoValidationSuffixes("redhat6")[0].startsWith("/"));
        request.setBaseUrl(baseUrl);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(baseUrl, repo.getBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

        baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        // variation #3: url with no trailing slash, suffix no prededing slash
        Assert.assertTrue(
                !baseUrl.endsWith("/") && !configuration.getRepoValidationSuffixes("redhat6")[0].startsWith("/"));
        request.setBaseUrl(baseUrl);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(baseUrl, repo.getBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

        // variation #4: url with no trailing slash, suffix preceding slash
        backingProperties.setProperty(Configuration.REPO_SUFFIX_KEY_DEFAULT, "/repodata/repomd.xml");
        Assert.assertTrue(
                !baseUrl.endsWith("/") && configuration.getRepoValidationSuffixes("suse11")[0].startsWith("/"));
        request.setBaseUrl(baseUrl);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(baseUrl, repo.getBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

        // variation #5: multiple suffix tests
        backingProperties.setProperty(Configuration.REPO_SUFFIX_KEY_UBUNTU, "/foo/bar.xml,/repodata/repomd.xml");
        Assert.assertTrue(configuration.getRepoValidationSuffixes("ubuntu12").length > 1);
        request.setBaseUrl(baseUrl);
        try {
            controller.updateRepositories(requests);
            Assert.assertEquals(baseUrl, repo.getBaseUrl());
        } catch (Exception e) {
            String exceptionMsg = e.getMessage();
            assertTrue(exceptionMsg.contains("Could not access base url"));
            LOG.error("Can not complete test. " + exceptionMsg);
        }

    }

    @Test
    public void testDeleteHostComponentInVariousStates() throws Exception {
        String clusterName = "foo1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-1.3.1"));
        String serviceName = "HDFS";
        String mapred = "MAPREDUCE";
        createService(clusterName, serviceName, null);
        createService(clusterName, mapred, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";
        String componentName4 = "JOBTRACKER";
        String componentName5 = "TASKTRACKER";
        String componentName6 = "MAPREDUCE_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);
        createServiceComponent(clusterName, mapred, componentName4, State.INIT);
        createServiceComponent(clusterName, mapred, componentName5, State.INIT);
        createServiceComponent(clusterName, mapred, componentName6, State.INIT);

        String host1 = "h1";

        addHost(host1, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, mapred, componentName4, host1, null);
        createServiceComponentHost(clusterName, mapred, componentName5, host1, null);
        createServiceComponentHost(clusterName, mapred, componentName6, host1, null);

        // Install
        installService(clusterName, serviceName, false, false);
        installService(clusterName, mapred, false, false);

        Cluster cluster = clusters.getCluster(clusterName);
        Service s1 = cluster.getService(serviceName);
        Service s2 = cluster.getService(mapred);
        ServiceComponent sc1 = s1.getServiceComponent(componentName1);
        sc1.getServiceComponentHosts().values().iterator().next().setState(State.STARTED);

        Set<ServiceComponentHostRequest> schRequests = new HashSet<ServiceComponentHostRequest>();
        // delete HC
        schRequests.clear();
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        try {
            controller.deleteHostComponents(schRequests);
            Assert.fail("Expect failure while deleting.");
        } catch (Exception ex) {
            Assert.assertTrue(ex.getMessage().contains("Host Component cannot be removed"));
        }

        sc1.getServiceComponentHosts().values().iterator().next().setDesiredState(State.STARTED);
        sc1.getServiceComponentHosts().values().iterator().next().setState(State.UNKNOWN);
        ServiceComponent sc2 = s1.getServiceComponent(componentName2);
        sc2.getServiceComponentHosts().values().iterator().next().setState(State.INIT);
        ServiceComponent sc3 = s1.getServiceComponent(componentName3);
        sc3.getServiceComponentHosts().values().iterator().next().setState(State.INSTALL_FAILED);
        ServiceComponent sc4 = s2.getServiceComponent(componentName4);
        sc4.getServiceComponentHosts().values().iterator().next().setDesiredState(State.INSTALLED);
        sc4.getServiceComponentHosts().values().iterator().next().setState(State.DISABLED);
        ServiceComponent sc5 = s2.getServiceComponent(componentName5);
        sc5.getServiceComponentHosts().values().iterator().next().setState(State.INSTALLED);
        ServiceComponent sc6 = s2.getServiceComponent(componentName6);
        sc6.getServiceComponentHosts().values().iterator().next().setState(State.INIT);

        schRequests.clear();
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName2, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName3, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, mapred, componentName4, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, mapred, componentName5, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, mapred, componentName6, host1, null));
        controller.deleteHostComponents(schRequests);
    }

    @Test
    public void testDeleteHost() throws Exception {
        String clusterName = "foo1";

        createCluster(clusterName);

        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));

        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";

        String host2 = "h2";
        clusters.addHost(host2);
        setOsFamily(clusters.getHost("h2"), "redhat", "6.3");
        clusters.getHost("h2").persist();

        String host3 = "h3";

        addHost(host1, clusterName);

        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // make them believe they are up
        Map<String, ServiceComponentHost> hostComponents = cluster.getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHosts();
        for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
            ServiceComponentHost cHost = entry.getValue();
            cHost.handleEvent(
                    new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(), cHost.getHostName(),
                            System.currentTimeMillis(), cluster.getDesiredStackVersion().getStackId()));
            cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                    cHost.getHostName(), System.currentTimeMillis()));
        }
        hostComponents = cluster.getService(serviceName).getServiceComponent(componentName2)
                .getServiceComponentHosts();
        for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
            ServiceComponentHost cHost = entry.getValue();
            cHost.handleEvent(
                    new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(), cHost.getHostName(),
                            System.currentTimeMillis(), cluster.getDesiredStackVersion().getStackId()));
            cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                    cHost.getHostName(), System.currentTimeMillis()));
        }

        Set<HostRequest> requests = new HashSet<HostRequest>();
        // delete from cluster
        requests.clear();
        requests.add(new HostRequest(host1, clusterName, null));
        try {
            HostResourceProviderTest.deleteHosts(controller, requests);
            fail("Expect failure deleting hosts when components exist.");
        } catch (Exception e) {
        }

        Set<ServiceComponentHostRequest> schRequests = new HashSet<ServiceComponentHostRequest>();
        // disable HC for non-clients
        schRequests
                .add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, "DISABLED"));
        schRequests
                .add(new ServiceComponentHostRequest(clusterName, serviceName, componentName2, host1, "DISABLED"));
        updateHostComponents(schRequests, new HashMap<String, String>(), false);

        // delete HC
        schRequests.clear();
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName2, host1, null));
        schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName3, host1, null));
        controller.deleteHostComponents(schRequests);

        Assert.assertEquals(0, cluster.getServiceComponentHosts(host1).size());

        // delete, which should fail since it is part of a cluster
        requests.clear();
        requests.add(new HostRequest(host1, null, null));
        try {
            HostResourceProviderTest.deleteHosts(controller, requests);
            fail("Expect failure when removing from host when it is part of a cluster.");
        } catch (Exception e) {
        }

        // delete host from cluster
        requests.clear();
        requests.add(new HostRequest(host1, clusterName, null));
        HostResourceProviderTest.deleteHosts(controller, requests);

        // host is no longer part of the cluster
        Assert.assertFalse(clusters.getHostsForCluster(clusterName).containsKey(host1));
        Assert.assertFalse(clusters.getClustersForHost(host1).contains(cluster));

        // delete entirely
        requests.clear();
        requests.add(new HostRequest(host1, null, null));
        HostResourceProviderTest.deleteHosts(controller, requests);

        // verify host does not exist
        try {
            clusters.getHost(host1);
            Assert.fail("Expected a HostNotFoundException.");
        } catch (HostNotFoundException e) {
            // expected
        }

        // remove host2
        requests.clear();
        requests.add(new HostRequest(host2, null, null));
        HostResourceProviderTest.deleteHosts(controller, requests);

        // verify host does not exist
        try {
            clusters.getHost(host2);
            Assert.fail("Expected a HostNotFoundException.");
        } catch (HostNotFoundException e) {
            // expected
        }

        // try removing a host that never existed
        requests.clear();
        requests.add(new HostRequest(host3, null, null));
        try {
            HostResourceProviderTest.deleteHosts(controller, requests);
            Assert.fail("Expected a HostNotFoundException trying to remove a host that was never added.");
        } catch (HostNotFoundException e) {
            // expected
        }

    }

    @Test
    public void testGetRootServices() throws Exception {

        RootServiceRequest request = new RootServiceRequest(null);
        Set<RootServiceResponse> responses = controller.getRootServices(Collections.singleton(request));
        Assert.assertEquals(RootServiceResponseFactory.Services.values().length, responses.size());

        RootServiceRequest requestWithParams = new RootServiceRequest(
                RootServiceResponseFactory.Services.AMBARI.toString());
        Set<RootServiceResponse> responsesWithParams = controller
                .getRootServices(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (RootServiceResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getServiceName(),
                    RootServiceResponseFactory.Services.AMBARI.toString());
        }

        RootServiceRequest invalidRequest = new RootServiceRequest(NON_EXT_VALUE);
        try {
            controller.getRootServices(Collections.singleton(invalidRequest));
        } catch (ObjectNotFoundException e) {
            // do nothing
        }
    }

    @Test
    public void testGetRootServiceComponents() throws Exception {

        RootServiceComponentRequest request = new RootServiceComponentRequest(
                RootServiceResponseFactory.Services.AMBARI.toString(), null);
        Set<RootServiceComponentResponse> responses = controller
                .getRootServiceComponents(Collections.singleton(request));
        Assert.assertEquals(RootServiceResponseFactory.Services.AMBARI.getComponents().length, responses.size());

        RootServiceComponentRequest requestWithParams = new RootServiceComponentRequest(
                RootServiceResponseFactory.Services.AMBARI.toString(),
                RootServiceResponseFactory.Services.AMBARI.getComponents()[0].toString());

        Set<RootServiceComponentResponse> responsesWithParams = controller
                .getRootServiceComponents(Collections.singleton(requestWithParams));
        Assert.assertEquals(1, responsesWithParams.size());
        for (RootServiceComponentResponse responseWithParams : responsesWithParams) {
            Assert.assertEquals(responseWithParams.getComponentName(),
                    RootServiceResponseFactory.Services.AMBARI.getComponents()[0].toString());
        }

        RootServiceComponentRequest invalidRequest = new RootServiceComponentRequest(NON_EXT_VALUE, NON_EXT_VALUE);
        try {
            controller.getRootServiceComponents(Collections.singleton(invalidRequest));
        } catch (ObjectNotFoundException e) {
            // do nothing
        }
    }

    @Test
    public void testDeleteComponentsOnHost() throws Exception {
        String clusterName = "foo1";

        createCluster(clusterName);

        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));

        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";

        addHost(host1, clusterName);

        createServiceComponentHost(clusterName, null, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // make them believe they are up
        Map<String, ServiceComponentHost> hostComponents = cluster.getService(serviceName)
                .getServiceComponent(componentName1).getServiceComponentHosts();
        for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
            ServiceComponentHost cHost = entry.getValue();
            cHost.handleEvent(
                    new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(), cHost.getHostName(),
                            System.currentTimeMillis(), cluster.getDesiredStackVersion().getStackId()));
            cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                    cHost.getHostName(), System.currentTimeMillis()));
        }
        hostComponents = cluster.getService(serviceName).getServiceComponent(componentName2)
                .getServiceComponentHosts();
        for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
            ServiceComponentHost cHost = entry.getValue();
            cHost.handleEvent(
                    new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(), cHost.getHostName(),
                            System.currentTimeMillis(), cluster.getDesiredStackVersion().getStackId()));
            cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                    cHost.getHostName(), System.currentTimeMillis()));
        }

        ServiceComponentHost sch = cluster.getService(serviceName).getServiceComponent(componentName2)
                .getServiceComponentHost(host1);
        Assert.assertNotNull(sch);

        sch.handleEvent(new ServiceComponentHostStartEvent(sch.getServiceComponentName(), sch.getHostName(),
                System.currentTimeMillis()));
        sch.handleEvent(new ServiceComponentHostStartedEvent(sch.getServiceComponentName(), sch.getHostName(),
                System.currentTimeMillis()));

        Set<ServiceComponentHostRequest> schRequests = new HashSet<ServiceComponentHostRequest>();
        schRequests.add(new ServiceComponentHostRequest(clusterName, null, null, host1, null));
        try {
            controller.deleteHostComponents(schRequests);
            fail("Expected exception while deleting all host components.");
        } catch (AmbariException e) {
        }
        Assert.assertEquals(3, cluster.getServiceComponentHosts(host1).size());

        sch.handleEvent(new ServiceComponentHostStopEvent(sch.getServiceComponentName(), sch.getHostName(),
                System.currentTimeMillis()));
        sch.handleEvent(new ServiceComponentHostStoppedEvent(sch.getServiceComponentName(), sch.getHostName(),
                System.currentTimeMillis()));

        schRequests.clear();
        // disable HC, DN was already stopped
        schRequests
                .add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, "DISABLED"));
        updateHostComponents(schRequests, new HashMap<String, String>(), false);

        // delete HC
        schRequests.clear();
        schRequests.add(new ServiceComponentHostRequest(clusterName, null, null, host1, null));
        controller.deleteHostComponents(schRequests);

        Assert.assertEquals(0, cluster.getServiceComponentHosts(host1).size());
    }

    @Test
    public void testExecutionCommandConfiguration() throws AmbariException {
        Map<String, Map<String, String>> config = new HashMap<String, Map<String, String>>();
        config.put("type1", new HashMap<String, String>());
        config.put("type3", new HashMap<String, String>());
        config.get("type3").put("name1", "neverchange");
        configHelper.applyCustomConfig(config, "type1", "name1", "value11", false);
        Assert.assertEquals("value11", config.get("type1").get("name1"));

        config.put("type1", new HashMap<String, String>());
        configHelper.applyCustomConfig(config, "type1", "name1", "value12", false);
        Assert.assertEquals("value12", config.get("type1").get("name1"));

        configHelper.applyCustomConfig(config, "type2", "name2", "value21", false);
        Assert.assertEquals("value21", config.get("type2").get("name2"));

        configHelper.applyCustomConfig(config, "type2", "name2", "", true);
        Assert.assertEquals("", config.get("type2").get("DELETED_name2"));
        Assert.assertEquals("neverchange", config.get("type3").get("name1"));

        Map<String, String> persistedClusterConfig = new HashMap<String, String>();
        persistedClusterConfig.put("name1", "value11");
        persistedClusterConfig.put("name3", "value31");
        persistedClusterConfig.put("name4", "value41");
        Map<String, String> override = new HashMap<String, String>();
        override.put("name1", "value12");
        override.put("name2", "value21");
        override.put("DELETED_name3", "value31");
        Map<String, String> mergedConfig = configHelper.getMergedConfig(persistedClusterConfig, override);
        Assert.assertEquals(3, mergedConfig.size());
        Assert.assertFalse(mergedConfig.containsKey("name3"));
        Assert.assertEquals("value12", mergedConfig.get("name1"));
        Assert.assertEquals("value21", mergedConfig.get("name2"));
        Assert.assertEquals("value41", mergedConfig.get("name4"));
    }

    @Test
    public void testApplyConfigurationWithTheSameTag() {
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                Properties properties = new Properties();
                properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");
                properties.setProperty(Configuration.METADETA_DIR_PATH, "src/test/resources/stacks");
                properties.setProperty(Configuration.SERVER_VERSION_FILE, "src/test/resources/version");
                properties.setProperty(Configuration.OS_VERSION_KEY, "centos6");
                properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");
                try {
                    install(new ControllerModule(properties));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        injector.getInstance(GuiceJpaInitializer.class);

        try {
            String tag = "version1";
            String type = "core-site";
            AmbariException exception = null;
            try {
                AmbariManagementController amc = injector.getInstance(AmbariManagementController.class);
                Clusters clusters = injector.getInstance(Clusters.class);
                Gson gson = new Gson();

                clusters.addHost("host1");
                clusters.addHost("host2");
                clusters.addHost("host3");
                Host host = clusters.getHost("host1");
                setOsFamily(host, "redhat", "6.3");
                host.persist();
                host = clusters.getHost("host2");
                setOsFamily(host, "redhat", "6.3");
                host.persist();
                host = clusters.getHost("host3");
                setOsFamily(host, "redhat", "6.3");
                host.persist();

                ClusterRequest clusterRequest = new ClusterRequest(null, "c1", "HDP-1.2.0", null);
                amc.createCluster(clusterRequest);

                Set<ServiceRequest> serviceRequests = new HashSet<ServiceRequest>();
                serviceRequests.add(new ServiceRequest("c1", "HDFS", null));

                ServiceResourceProviderTest.createServices(amc, serviceRequests);

                Type confType = new TypeToken<Map<String, String>>() {
                }.getType();

                ConfigurationRequest configurationRequest = new ConfigurationRequest("c1", type, tag,
                        gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType),
                        null);
                amc.createConfiguration(configurationRequest);

                amc.createConfiguration(configurationRequest);
            } catch (AmbariException e) {
                exception = e;
            }

            assertNotNull(exception);
            String exceptionMessage = MessageFormat.format("Configuration with tag ''{0}'' exists for ''{1}''", tag,
                    type);
            org.junit.Assert.assertEquals(exceptionMessage, exception.getMessage());
        } finally {
            injector.getInstance(PersistService.class).stop();
        }
    }

    @Test
    public void testDeleteClusterCreateHost() throws Exception {

        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                Properties properties = new Properties();
                properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");

                properties.setProperty(Configuration.METADETA_DIR_PATH, "src/test/resources/stacks");
                properties.setProperty(Configuration.SERVER_VERSION_FILE, "../version");
                properties.setProperty(Configuration.OS_VERSION_KEY, "centos6");
                properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");
                try {
                    install(new ControllerModule(properties));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        injector.getInstance(GuiceJpaInitializer.class);

        String STACK_ID = "HDP-2.0.1";
        Long CLUSTER_ID = 1L;
        String CLUSTER_NAME = "c1";
        String HOST1 = "h1";
        String HOST2 = "h2";

        try {
            Clusters clusters = injector.getInstance(Clusters.class);

            clusters.addHost(HOST1);
            Host host = clusters.getHost(HOST1);
            setOsFamily(host, "redhat", "6.3");
            host.persist();

            clusters.addHost(HOST2);
            host = clusters.getHost(HOST2);
            setOsFamily(host, "redhat", "6.3");
            host.persist();

            AmbariManagementController amc = injector.getInstance(AmbariManagementController.class);

            ClusterRequest cr = new ClusterRequest(null, CLUSTER_NAME, STACK_ID, null);
            amc.createCluster(cr);

            ConfigurationRequest configRequest = new ConfigurationRequest(CLUSTER_NAME, "global", "version1",
                    new HashMap<String, String>() {
                        {
                            put("a", "b");
                        }
                    }, null);
            cr.setDesiredConfig(Collections.singletonList(configRequest));
            cr.setClusterId(CLUSTER_ID);
            amc.updateClusters(Collections.singleton(cr), new HashMap<String, String>());

            // add some hosts
            Set<HostRequest> hrs = new HashSet<HostRequest>();
            hrs.add(new HostRequest(HOST1, CLUSTER_NAME, null));
            HostResourceProviderTest.createHosts(amc, hrs);

            Set<ServiceRequest> serviceRequests = new HashSet<ServiceRequest>();
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "HDFS", null));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "MAPREDUCE2", null));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "YARN", null));

            ServiceResourceProviderTest.createServices(amc, serviceRequests);

            Set<ServiceComponentRequest> serviceComponentRequests = new HashSet<ServiceComponentRequest>();
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "NAMENODE", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "SECONDARY_NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "DATANODE", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "MAPREDUCE2", "HISTORYSERVER", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "YARN", "RESOURCEMANAGER", null));
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "YARN", "NODEMANAGER", null));
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "HDFS_CLIENT", null));

            ComponentResourceProviderTest.createComponents(amc, serviceComponentRequests);

            Set<ServiceComponentHostRequest> componentHostRequests = new HashSet<ServiceComponentHostRequest>();
            componentHostRequests.add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "DATANODE", HOST1, null));
            componentHostRequests.add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "NAMENODE", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "SECONDARY_NAMENODE", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "HISTORYSERVER", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "RESOURCEMANAGER", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "NODEMANAGER", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "HDFS_CLIENT", HOST1, null));

            amc.createHostComponents(componentHostRequests);

            RequestResourceFilter resourceFilter = new RequestResourceFilter("HDFS", null, null);
            ExecuteActionRequest ar = new ExecuteActionRequest(CLUSTER_NAME, Role.HDFS_SERVICE_CHECK.name(), null,
                    false);
            ar.getResourceFilters().add(resourceFilter);
            amc.createAction(ar, null);

            // change mind, delete the cluster
            amc.deleteCluster(cr);

            assertNotNull(clusters.getHost(HOST1));
            assertNotNull(clusters.getHost(HOST2));

            HostDAO dao = injector.getInstance(HostDAO.class);

            assertNotNull(dao.findByName(HOST1));
            assertNotNull(dao.findByName(HOST2));

        } finally {
            injector.getInstance(PersistService.class).stop();
        }
    }

    @Test
    public void testDisableAndDeleteStates() throws Exception {
        Map<String, String> mapRequestProps = new HashMap<String, String>();
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                Properties properties = new Properties();
                properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");
                properties.setProperty(Configuration.METADETA_DIR_PATH, "src/test/resources/stacks");
                properties.setProperty(Configuration.SERVER_VERSION_FILE, "src/test/resources/version");
                properties.setProperty(Configuration.OS_VERSION_KEY, "centos5");
                properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");

                try {
                    install(new ControllerModule(properties));

                    // ambari events interfere with the workflow of this test
                    bind(AmbariEventPublisher.class).toInstance(EasyMock.createMock(AmbariEventPublisher.class));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        injector.getInstance(GuiceJpaInitializer.class);

        try {
            AmbariManagementController amc = injector.getInstance(AmbariManagementController.class);
            Clusters clusters = injector.getInstance(Clusters.class);
            Gson gson = new Gson();

            clusters.addHost("host1");
            clusters.addHost("host2");
            clusters.addHost("host3");
            Host host = clusters.getHost("host1");
            setOsFamily(host, "redhat", "5.9");
            host.persist();
            host = clusters.getHost("host2");
            setOsFamily(host, "redhat", "5.9");
            host.persist();
            host = clusters.getHost("host3");
            setOsFamily(host, "redhat", "5.9");
            host.persist();

            ClusterRequest clusterRequest = new ClusterRequest(null, "c1", "HDP-1.2.0", null);
            amc.createCluster(clusterRequest);

            Set<ServiceRequest> serviceRequests = new HashSet<ServiceRequest>();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", null));
            serviceRequests.add(new ServiceRequest("c1", "HIVE", null));

            ServiceResourceProviderTest.createServices(amc, serviceRequests);

            Type confType = new TypeToken<Map<String, String>>() {
            }.getType();

            ConfigurationRequest configurationRequest = new ConfigurationRequest("c1", "core-site", "version1",
                    gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);

            configurationRequest = new ConfigurationRequest("c1", "hdfs-site", "version1",
                    gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);

            configurationRequest = new ConfigurationRequest("c1", "global", "version1",
                    gson.<Map<String, String>>fromJson("{ \"hive.server2.enable.doAs\" : \"true\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);

            Assert.assertTrue(clusters.getCluster("c1").getDesiredConfigs().containsKey("hive-site"));

            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", null));

            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);

            Set<ServiceComponentRequest> serviceComponentRequests = new HashSet<ServiceComponentRequest>();
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "SECONDARY_NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "DATANODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "HDFS_CLIENT", null));

            ComponentResourceProviderTest.createComponents(amc, serviceComponentRequests);

            Set<HostRequest> hostRequests = new HashSet<HostRequest>();
            hostRequests.add(new HostRequest("host1", "c1", null));
            hostRequests.add(new HostRequest("host2", "c1", null));
            hostRequests.add(new HostRequest("host3", "c1", null));

            HostResourceProviderTest.createHosts(amc, hostRequests);

            Set<ServiceComponentHostRequest> componentHostRequests = new HashSet<ServiceComponentHostRequest>();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host1", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest("c1", null, "SECONDARY_NAMENODE", "host1", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host2", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host3", null));

            amc.createHostComponents(componentHostRequests);

            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", "INSTALLED"));
            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);

            Cluster cluster = clusters.getCluster("c1");
            Map<String, ServiceComponentHost> namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE")
                    .getServiceComponentHosts();
            org.junit.Assert.assertEquals(1, namenodes.size());

            ServiceComponentHost componentHost = namenodes.get("host1");

            Map<String, ServiceComponentHost> hostComponents = cluster.getService("HDFS")
                    .getServiceComponent("DATANODE").getServiceComponentHosts();
            for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
                ServiceComponentHost cHost = entry.getValue();
                cHost.handleEvent(new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis(), "HDP-1.2.0"));
                cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis()));
            }
            hostComponents = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
            for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
                ServiceComponentHost cHost = entry.getValue();
                cHost.handleEvent(new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis(), "HDP-1.2.0"));
                cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis()));
            }
            hostComponents = cluster.getService("HDFS").getServiceComponent("SECONDARY_NAMENODE")
                    .getServiceComponentHosts();
            for (Map.Entry<String, ServiceComponentHost> entry : hostComponents.entrySet()) {
                ServiceComponentHost cHost = entry.getValue();
                cHost.handleEvent(new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis(), "HDP-1.2.0"));
                cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                        cHost.getHostName(), System.currentTimeMillis()));
            }

            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "DISABLED"));

            updateHostComponents(amc, componentHostRequests, mapRequestProps, true);

            Assert.assertEquals(State.DISABLED, componentHost.getState());

            componentHostRequests.clear();
            componentHostRequests
                    .add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "INSTALLED"));

            updateHostComponents(amc, componentHostRequests, mapRequestProps, true);

            Assert.assertEquals(State.INSTALLED, componentHost.getState());

            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "DISABLED"));

            updateHostComponents(amc, componentHostRequests, mapRequestProps, true);

            Assert.assertEquals(State.DISABLED, componentHost.getState());

            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host2", null));

            amc.createHostComponents(componentHostRequests);

            componentHostRequests.clear();
            componentHostRequests
                    .add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host2", "INSTALLED"));

            updateHostComponents(amc, componentHostRequests, mapRequestProps, true);

            namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
            Assert.assertEquals(2, namenodes.size());

            componentHost = namenodes.get("host2");
            componentHost.handleEvent(new ServiceComponentHostInstallEvent(componentHost.getServiceComponentName(),
                    componentHost.getHostName(), System.currentTimeMillis(), "HDP-1.2.0"));
            componentHost
                    .handleEvent(new ServiceComponentHostOpSucceededEvent(componentHost.getServiceComponentName(),
                            componentHost.getHostName(), System.currentTimeMillis()));

            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", "STARTED"));

            RequestStatusResponse response = ServiceResourceProviderTest.updateServices(amc, serviceRequests,
                    mapRequestProps, true, false);
            for (ShortTaskStatus shortTaskStatus : response.getTasks()) {
                assertFalse("host1".equals(shortTaskStatus.getHostName())
                        && "NAMENODE".equals(shortTaskStatus.getRole()));
            }

            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", null));

            amc.deleteHostComponents(componentHostRequests);
            namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
            org.junit.Assert.assertEquals(1, namenodes.size());

            // testing the behavior for runSmokeTest flag
            // piggybacking on this test to avoid setting up the mock cluster
            testRunSmokeTestFlag(mapRequestProps, amc, serviceRequests);

            // should be able to add the host component back
            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", null));
            amc.createHostComponents(componentHostRequests);
            namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
            assertEquals(2, namenodes.size());

            // make INSTALLED again
            componentHost = namenodes.get("host1");
            componentHost.handleEvent(new ServiceComponentHostInstallEvent(componentHost.getServiceComponentName(),
                    componentHost.getHostName(), System.currentTimeMillis(), "HDP-1.2.0"));
            componentHost
                    .handleEvent(new ServiceComponentHostOpSucceededEvent(componentHost.getServiceComponentName(),
                            componentHost.getHostName(), System.currentTimeMillis()));
            componentHostRequests.clear();
            componentHostRequests
                    .add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "INSTALLED"));
            updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
            assertEquals(State.INSTALLED, namenodes.get("host1").getState());

            // make unknown
            ServiceComponentHost sch = null;
            for (ServiceComponentHost tmp : cluster.getServiceComponentHosts("host2")) {
                if (tmp.getServiceComponentName().equals("DATANODE")) {
                    tmp.setState(State.UNKNOWN);
                    sch = tmp;
                }
            }
            assertNotNull(sch);

            // make disabled
            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host2", "DISABLED"));
            updateHostComponents(amc, componentHostRequests, mapRequestProps, false);
            org.junit.Assert.assertEquals(State.DISABLED, sch.getState());

            // State should not be changed if componentHostRequests are empty
            componentHostRequests.clear();
            mapRequestProps.put(RequestOperationLevel.OPERATION_CLUSTER_ID, "c1");
            updateHostComponents(amc, componentHostRequests, mapRequestProps, false);
            org.junit.Assert.assertEquals(State.DISABLED, sch.getState());
            mapRequestProps.clear();

            // ServiceComponentHost remains in disabled after service stop
            assertEquals(sch.getServiceComponentName(), "DATANODE");
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", "INSTALLED"));
            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);
            assertEquals(State.DISABLED, sch.getState());

            // ServiceComponentHost remains in disabled after service start
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", "STARTED"));
            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);
            assertEquals(State.DISABLED, sch.getState());

            // confirm delete
            componentHostRequests.clear();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host2", null));
            amc.deleteHostComponents(componentHostRequests);

            sch = null;
            for (ServiceComponentHost tmp : cluster.getServiceComponentHosts("host2")) {
                if (tmp.getServiceComponentName().equals("DATANODE")) {
                    sch = tmp;
                }
            }
            org.junit.Assert.assertNull(sch);

            /*
            *Test remove service
            */
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", "INSTALLED"));
            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", null, null));
            org.junit.Assert.assertEquals(2, ServiceResourceProviderTest.getServices(amc, serviceRequests).size());
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", null));
            serviceRequests.add(new ServiceRequest("c1", "HIVE", null));
            ServiceResourceProviderTest.deleteServices(amc, serviceRequests);
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", null, null));
            org.junit.Assert.assertEquals(0, ServiceResourceProviderTest.getServices(amc, serviceRequests).size());

            /*
            *Test add service again
            */
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", null));

            ServiceResourceProviderTest.createServices(amc, serviceRequests);

            org.junit.Assert.assertEquals(1, ServiceResourceProviderTest.getServices(amc, serviceRequests).size());
            //Create new configs
            configurationRequest = new ConfigurationRequest("c1", "core-site", "version2",
                    gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);
            configurationRequest = new ConfigurationRequest("c1", "hdfs-site", "version2",
                    gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);
            configurationRequest = new ConfigurationRequest("c1", "global", "version2",
                    gson.<Map<String, String>>fromJson("{ \"hbase_hdfs_root_dir\" : \"/apps/hbase/\"}", confType),
                    null);
            amc.createConfiguration(configurationRequest);
            //Add configs to service
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest("c1", "HDFS", null));
            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);
            //Crate service components
            serviceComponentRequests = new HashSet<ServiceComponentRequest>();
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "SECONDARY_NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "DATANODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest("c1", "HDFS", "HDFS_CLIENT", null));
            ComponentResourceProviderTest.createComponents(amc, serviceComponentRequests);

            //Create ServiceComponentHosts
            componentHostRequests = new HashSet<ServiceComponentHostRequest>();
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host1", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest("c1", null, "SECONDARY_NAMENODE", "host1", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host2", null));
            componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host3", null));
            amc.createHostComponents(componentHostRequests);

            namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
            org.junit.Assert.assertEquals(1, namenodes.size());
            Map<String, ServiceComponentHost> datanodes = cluster.getService("HDFS").getServiceComponent("DATANODE")
                    .getServiceComponentHosts();
            org.junit.Assert.assertEquals(3, datanodes.size());
            Map<String, ServiceComponentHost> namenodes2 = cluster.getService("HDFS")
                    .getServiceComponent("SECONDARY_NAMENODE").getServiceComponentHosts();
            org.junit.Assert.assertEquals(1, namenodes2.size());
        } finally {
            injector.getInstance(PersistService.class).stop();
        }
    }

    @Test
    public void testScheduleSmokeTest() throws Exception {

        final String HOST1 = "host1";
        final String OS_TYPE = "centos5";
        final String STACK_ID = "HDP-2.0.1";
        final String CLUSTER_NAME = "c1";
        final String HDFS_SERVICE_CHECK_ROLE = "HDFS_SERVICE_CHECK";
        final String MAPREDUCE2_SERVICE_CHECK_ROLE = "MAPREDUCE2_SERVICE_CHECK";
        final String YARN_SERVICE_CHECK_ROLE = "YARN_SERVICE_CHECK";

        Map<String, String> mapRequestProps = Collections.emptyMap();
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                Properties properties = new Properties();
                properties.setProperty(Configuration.SERVER_PERSISTENCE_TYPE_KEY, "in-memory");

                properties.setProperty(Configuration.METADETA_DIR_PATH, "src/test/resources/stacks");
                properties.setProperty(Configuration.SERVER_VERSION_FILE, "../version");
                properties.setProperty(Configuration.OS_VERSION_KEY, OS_TYPE);
                properties.setProperty(Configuration.SHARED_RESOURCES_DIR_KEY, "src/test/resources/");
                try {
                    install(new ControllerModule(properties));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        injector.getInstance(GuiceJpaInitializer.class);

        try {
            AmbariManagementController amc = injector.getInstance(AmbariManagementController.class);
            Clusters clusters = injector.getInstance(Clusters.class);

            clusters.addHost(HOST1);
            Host host = clusters.getHost(HOST1);
            setOsFamily(host, "redhat", "5.9");
            host.persist();

            ClusterRequest clusterRequest = new ClusterRequest(null, CLUSTER_NAME, STACK_ID, null);
            amc.createCluster(clusterRequest);

            Set<ServiceRequest> serviceRequests = new HashSet<ServiceRequest>();
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "HDFS", null));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "MAPREDUCE2", null));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "YARN", null));

            ServiceResourceProviderTest.createServices(amc, serviceRequests);

            Set<ServiceComponentRequest> serviceComponentRequests = new HashSet<ServiceComponentRequest>();
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "NAMENODE", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "SECONDARY_NAMENODE", null));
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "HDFS", "DATANODE", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "MAPREDUCE2", "HISTORYSERVER", null));
            serviceComponentRequests
                    .add(new ServiceComponentRequest(CLUSTER_NAME, "YARN", "RESOURCEMANAGER", null));
            serviceComponentRequests.add(new ServiceComponentRequest(CLUSTER_NAME, "YARN", "NODEMANAGER", null));

            ComponentResourceProviderTest.createComponents(amc, serviceComponentRequests);

            Set<HostRequest> hostRequests = new HashSet<HostRequest>();
            hostRequests.add(new HostRequest(HOST1, CLUSTER_NAME, null));

            HostResourceProviderTest.createHosts(amc, hostRequests);

            Set<ServiceComponentHostRequest> componentHostRequests = new HashSet<ServiceComponentHostRequest>();
            componentHostRequests.add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "DATANODE", HOST1, null));
            componentHostRequests.add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "NAMENODE", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "SECONDARY_NAMENODE", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "HISTORYSERVER", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "RESOURCEMANAGER", HOST1, null));
            componentHostRequests
                    .add(new ServiceComponentHostRequest(CLUSTER_NAME, null, "NODEMANAGER", HOST1, null));

            amc.createHostComponents(componentHostRequests);

            //Install services
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "HDFS", State.INSTALLED.name()));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "MAPREDUCE2", State.INSTALLED.name()));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "YARN", State.INSTALLED.name()));

            ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, true, false);

            Cluster cluster = clusters.getCluster(CLUSTER_NAME);

            for (String serviceName : cluster.getServices().keySet()) {

                for (String componentName : cluster.getService(serviceName).getServiceComponents().keySet()) {

                    Map<String, ServiceComponentHost> serviceComponentHosts = cluster.getService(serviceName)
                            .getServiceComponent(componentName).getServiceComponentHosts();

                    for (Map.Entry<String, ServiceComponentHost> entry : serviceComponentHosts.entrySet()) {
                        ServiceComponentHost cHost = entry.getValue();
                        cHost.handleEvent(new ServiceComponentHostInstallEvent(cHost.getServiceComponentName(),
                                cHost.getHostName(), System.currentTimeMillis(), STACK_ID));
                        cHost.handleEvent(new ServiceComponentHostOpSucceededEvent(cHost.getServiceComponentName(),
                                cHost.getHostName(), System.currentTimeMillis()));
                    }
                }
            }

            //Start services
            serviceRequests.clear();
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "HDFS", State.STARTED.name()));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "MAPREDUCE2", State.STARTED.name()));
            serviceRequests.add(new ServiceRequest(CLUSTER_NAME, "YARN", State.STARTED.name()));

            RequestStatusResponse response = ServiceResourceProviderTest.updateServices(amc, serviceRequests,
                    mapRequestProps, true, false);

            Collection<?> hdfsSmokeTasks = CollectionUtils.select(response.getTasks(),
                    new RolePredicate(HDFS_SERVICE_CHECK_ROLE));
            //Ensure that smoke test task was created for HDFS
            org.junit.Assert.assertEquals(1, hdfsSmokeTasks.size());

            Collection<?> mapreduce2SmokeTasks = CollectionUtils.select(response.getTasks(),
                    new RolePredicate(MAPREDUCE2_SERVICE_CHECK_ROLE));
            //Ensure that smoke test task was created for MAPREDUCE2
            org.junit.Assert.assertEquals(1, mapreduce2SmokeTasks.size());

            Collection<?> yarnSmokeTasks = CollectionUtils.select(response.getTasks(),
                    new RolePredicate(YARN_SERVICE_CHECK_ROLE));
            //Ensure that smoke test task was created for YARN
            org.junit.Assert.assertEquals(1, yarnSmokeTasks.size());
        } finally {
            injector.getInstance(PersistService.class).stop();
        }
    }

    @Test
    public void testGetServices2() throws Exception {
        // member state mocks
        Injector injector = createStrictMock(Injector.class);
        Capture<AmbariManagementController> controllerCapture = new Capture<AmbariManagementController>();
        Clusters clusters = createNiceMock(Clusters.class);
        MaintenanceStateHelper maintHelper = createNiceMock(MaintenanceStateHelper.class);

        Cluster cluster = createNiceMock(Cluster.class);
        Service service = createNiceMock(Service.class);
        ServiceResponse response = createNiceMock(ServiceResponse.class);

        // requests
        ServiceRequest request1 = new ServiceRequest("cluster1", "service1", null);

        Set<ServiceRequest> setRequests = new HashSet<ServiceRequest>();
        setRequests.add(request1);

        // expectations
        // constructor init
        injector.injectMembers(capture(controllerCapture));
        expect(injector.getInstance(Gson.class)).andReturn(null);
        expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
        expect(injector.getInstance(KerberosHelper.class)).andReturn(createStrictMock(KerberosHelper.class));

        // getServices
        expect(clusters.getCluster("cluster1")).andReturn(cluster);
        expect(cluster.getService("service1")).andReturn(service);

        expect(service.convertToResponse()).andReturn(response);
        // replay mocks
        replay(maintHelper, injector, clusters, cluster, service, response);

        //test
        AmbariManagementController controller = new AmbariManagementControllerImpl(null, clusters, injector);
        Set<ServiceResponse> setResponses = ServiceResourceProviderTest.getServices(controller, setRequests);

        // assert and verify
        assertSame(controller, controllerCapture.getValue());
        assertEquals(1, setResponses.size());
        assertTrue(setResponses.contains(response));

        verify(injector, clusters, cluster, service, response);
    }

    /**
     * Ensure that ServiceNotFoundException is propagated in case where there is a single request.
     */
    @Test
    public void testGetServices___ServiceNotFoundException() throws Exception {
        // member state mocks
        Injector injector = createStrictMock(Injector.class);
        Capture<AmbariManagementController> controllerCapture = new Capture<AmbariManagementController>();
        Clusters clusters = createNiceMock(Clusters.class);
        MaintenanceStateHelper maintHelper = createNiceMock(MaintenanceStateHelper.class);
        Cluster cluster = createNiceMock(Cluster.class);

        // requests
        ServiceRequest request1 = new ServiceRequest("cluster1", "service1", null);
        Set<ServiceRequest> setRequests = new HashSet<ServiceRequest>();
        setRequests.add(request1);

        // expectations
        // constructor init
        injector.injectMembers(capture(controllerCapture));
        expect(injector.getInstance(Gson.class)).andReturn(null);
        expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
        expect(injector.getInstance(KerberosHelper.class)).andReturn(createStrictMock(KerberosHelper.class));

        // getServices
        expect(clusters.getCluster("cluster1")).andReturn(cluster);
        expect(cluster.getService("service1")).andThrow(new ServiceNotFoundException("custer1", "service1"));

        // replay mocks
        replay(maintHelper, injector, clusters, cluster);

        //test
        AmbariManagementController controller = new AmbariManagementControllerImpl(null, clusters, injector);

        // assert that exception is thrown in case where there is a single request
        try {
            ServiceResourceProviderTest.getServices(controller, setRequests);
            fail("expected ServiceNotFoundException");
        } catch (ServiceNotFoundException e) {
            // expected
        }

        assertSame(controller, controllerCapture.getValue());
        verify(injector, clusters, cluster);
    }

    /**
     * Ensure that ServiceNotFoundException is handled where there are multiple requests as would be the
     * case when an OR predicate is provided in the query.
     */
    @Test
    public void testGetServices___OR_Predicate_ServiceNotFoundException() throws Exception {
        // member state mocks
        Injector injector = createStrictMock(Injector.class);
        Capture<AmbariManagementController> controllerCapture = new Capture<AmbariManagementController>();
        Clusters clusters = createNiceMock(Clusters.class);
        MaintenanceStateHelper maintHelper = createNiceMock(MaintenanceStateHelper.class);

        Cluster cluster = createNiceMock(Cluster.class);
        Service service1 = createNiceMock(Service.class);
        Service service2 = createNiceMock(Service.class);
        ServiceResponse response = createNiceMock(ServiceResponse.class);
        ServiceResponse response2 = createNiceMock(ServiceResponse.class);

        // requests
        ServiceRequest request1 = new ServiceRequest("cluster1", "service1", null);
        ServiceRequest request2 = new ServiceRequest("cluster1", "service2", null);
        ServiceRequest request3 = new ServiceRequest("cluster1", "service3", null);
        ServiceRequest request4 = new ServiceRequest("cluster1", "service4", null);

        Set<ServiceRequest> setRequests = new HashSet<ServiceRequest>();
        setRequests.add(request1);
        setRequests.add(request2);
        setRequests.add(request3);
        setRequests.add(request4);

        // expectations
        // constructor init
        injector.injectMembers(capture(controllerCapture));
        expect(injector.getInstance(Gson.class)).andReturn(null);
        expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
        expect(injector.getInstance(KerberosHelper.class)).andReturn(createStrictMock(KerberosHelper.class));

        // getServices
        expect(clusters.getCluster("cluster1")).andReturn(cluster).times(4);
        expect(cluster.getService("service1")).andReturn(service1);
        expect(cluster.getService("service2")).andThrow(new ServiceNotFoundException("cluster1", "service2"));
        expect(cluster.getService("service3")).andThrow(new ServiceNotFoundException("cluster1", "service3"));
        expect(cluster.getService("service4")).andReturn(service2);

        expect(service1.convertToResponse()).andReturn(response);
        expect(service2.convertToResponse()).andReturn(response2);
        // replay mocks
        replay(maintHelper, injector, clusters, cluster, service1, service2, response, response2);

        //test
        AmbariManagementController controller = new AmbariManagementControllerImpl(null, clusters, injector);
        Set<ServiceResponse> setResponses = ServiceResourceProviderTest.getServices(controller, setRequests);

        // assert and verify
        assertSame(controller, controllerCapture.getValue());
        assertEquals(2, setResponses.size());
        assertTrue(setResponses.contains(response));
        assertTrue(setResponses.contains(response2));

        verify(injector, clusters, cluster, service1, service2, response, response2);
    }

    private void testRunSmokeTestFlag(Map<String, String> mapRequestProps, AmbariManagementController amc,
            Set<ServiceRequest> serviceRequests) throws AmbariException {
        RequestStatusResponse response;//Starting HDFS service. No run_smoke_test flag is set, smoke

        //Stopping HDFS service
        serviceRequests.clear();
        serviceRequests.add(new ServiceRequest("c1", "HDFS", "INSTALLED"));
        ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, false, false);

        //Starting HDFS service. No run_smoke_test flag is set, smoke
        // test(HDFS_SERVICE_CHECK) won't run
        boolean runSmokeTest = false;
        serviceRequests.clear();
        serviceRequests.add(new ServiceRequest("c1", "HDFS", "STARTED"));
        response = ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, runSmokeTest,
                false);

        List<ShortTaskStatus> taskStatuses = response.getTasks();
        boolean smokeTestRequired = false;
        for (ShortTaskStatus shortTaskStatus : taskStatuses) {
            if (shortTaskStatus.getRole().equals(Role.HDFS_SERVICE_CHECK.toString())) {
                smokeTestRequired = true;
            }
        }
        assertFalse(smokeTestRequired);

        //Stopping HDFS service
        serviceRequests.clear();
        serviceRequests.add(new ServiceRequest("c1", "HDFS", "INSTALLED"));
        ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, false, false);

        //Starting HDFS service again.
        //run_smoke_test flag is set, smoke test will be run
        runSmokeTest = true;
        serviceRequests.clear();
        serviceRequests.add(new ServiceRequest("c1", "HDFS", "STARTED"));
        response = ServiceResourceProviderTest.updateServices(amc, serviceRequests, mapRequestProps, runSmokeTest,
                false);

        taskStatuses = response.getTasks();
        smokeTestRequired = false;
        for (ShortTaskStatus shortTaskStatus : taskStatuses) {
            if (shortTaskStatus.getRole().equals(Role.HDFS_SERVICE_CHECK.toString())) {
                smokeTestRequired = true;
            }
        }
        assertTrue(smokeTestRequired);
    }

    private class RolePredicate implements org.apache.commons.collections.Predicate {

        private String role;

        public RolePredicate(String role) {
            this.role = role;
        }

        @Override
        public boolean evaluate(Object obj) {
            ShortTaskStatus task = (ShortTaskStatus) obj;
            return task.getRole().equals(role);
        }
    }

    @Test
    public void testReinstallClientSchSkippedInMaintenance() throws Exception {
        Cluster c1 = setupClusterWithHosts("c1", "HDP-1.2.0", new ArrayList<String>() {
            {
                add("h1");
                add("h2");
                add("h3");
            }
        }, "centos5");

        Service hdfs = c1.addService("HDFS");
        hdfs.persist();
        createServiceComponent("c1", "HDFS", "NAMENODE", State.INIT);
        createServiceComponent("c1", "HDFS", "DATANODE", State.INIT);
        createServiceComponent("c1", "HDFS", "HDFS_CLIENT", State.INIT);

        createServiceComponentHost("c1", "HDFS", "NAMENODE", "h1", State.INIT);
        createServiceComponentHost("c1", "HDFS", "DATANODE", "h1", State.INIT);
        createServiceComponentHost("c1", "HDFS", "HDFS_CLIENT", "h1", State.INIT);
        createServiceComponentHost("c1", "HDFS", "HDFS_CLIENT", "h2", State.INIT);
        createServiceComponentHost("c1", "HDFS", "HDFS_CLIENT", "h3", State.INIT);

        installService("c1", "HDFS", false, false);

        clusters.getHost("h3").setMaintenanceState(c1.getClusterId(), MaintenanceState.ON);

        Long id = startService("c1", "HDFS", false, true);

        Assert.assertNotNull(id);
        List<Stage> stages = actionDB.getAllStages(id);
        Assert.assertNotNull(stages);
        HostRoleCommand hrc1 = null;
        HostRoleCommand hrc2 = null;
        HostRoleCommand hrc3 = null;
        for (Stage s : stages) {
            for (HostRoleCommand hrc : s.getOrderedHostRoleCommands()) {
                if (hrc.getRole().equals(Role.HDFS_CLIENT) && hrc.getHostName().equals("h1")) {
                    hrc1 = hrc;
                } else if (hrc.getRole().equals(Role.HDFS_CLIENT) && hrc.getHostName().equals("h2")) {
                    hrc2 = hrc;
                } else if (hrc.getRole().equals(Role.HDFS_CLIENT) && hrc.getHostName().equals("h3")) {
                    hrc3 = hrc;
                }
            }
        }

        Assert.assertNotNull(hrc1);
        Assert.assertNotNull(hrc2);
        Assert.assertNull(hrc3);
    }

    @Test
    public void setMonitoringServicesRestartRequired() throws Exception {
        String clusterName = "c1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        StackId stackId = new StackId("HDP-2.0.8");
        cluster.setDesiredStackVersion(stackId);
        cluster.setCurrentStackVersion(stackId);

        String hdfsService = "HDFS";
        String fakeMonitoringService = "FAKENAGIOS";
        createService(clusterName, hdfsService, null);
        createService(clusterName, fakeMonitoringService, null);

        String namenode = "NAMENODE";
        String datanode = "DATANODE";
        String hdfsClient = "HDFS_CLIENT";
        String fakeServer = "FAKE_MONITORING_SERVER";

        createServiceComponent(clusterName, hdfsService, namenode, State.INIT);
        createServiceComponent(clusterName, hdfsService, datanode, State.INIT);
        createServiceComponent(clusterName, fakeMonitoringService, fakeServer, State.INIT);

        String host1 = "h1";

        addHost(host1, clusterName);
        createServiceComponentHost(clusterName, hdfsService, namenode, host1, null);
        createServiceComponentHost(clusterName, hdfsService, datanode, host1, null);
        createServiceComponentHost(clusterName, fakeMonitoringService, fakeServer, host1, null);

        ServiceComponentHost monitoringServiceComponentHost = null;
        for (ServiceComponentHost sch : cluster.getServiceComponentHosts(host1)) {
            if (sch.getServiceComponentName().equals(fakeServer)) {
                monitoringServiceComponentHost = sch;
            }
        }

        assertFalse(monitoringServiceComponentHost.isRestartRequired());

        createServiceComponent(clusterName, hdfsService, hdfsClient, State.INIT);

        createServiceComponentHost(clusterName, hdfsService, hdfsClient, host1, null);

        assertTrue(monitoringServiceComponentHost.isRestartRequired());
    }

    @Test
    public void setRestartRequiredAfterChangeService() throws Exception {
        String clusterName = "c1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        StackId stackId = new StackId("HDP-2.0.7");
        cluster.setDesiredStackVersion(stackId);
        cluster.setCurrentStackVersion(stackId);

        String hdfsService = "HDFS";
        String zookeeperService = "ZOOKEEPER";
        createService(clusterName, hdfsService, null);
        createService(clusterName, zookeeperService, null);

        String namenode = "NAMENODE";
        String datanode = "DATANODE";
        String hdfsClient = "HDFS_CLIENT";
        String zookeeperServer = "ZOOKEEPER_SERVER";
        createServiceComponent(clusterName, hdfsService, namenode, State.INIT);
        createServiceComponent(clusterName, hdfsService, datanode, State.INIT);
        createServiceComponent(clusterName, zookeeperService, zookeeperServer, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        createServiceComponentHost(clusterName, hdfsService, namenode, host1, null);
        createServiceComponentHost(clusterName, hdfsService, datanode, host1, null);
        createServiceComponentHost(clusterName, zookeeperService, zookeeperServer, host1, null);

        ServiceComponentHost zookeeperSch = null;
        for (ServiceComponentHost sch : cluster.getServiceComponentHosts(host1)) {
            if (sch.getServiceComponentName().equals(zookeeperServer)) {
                zookeeperSch = sch;
            }
        }
        assertFalse(zookeeperSch.isRestartRequired());

        addHost(host2, clusterName);
        createServiceComponentHost(clusterName, zookeeperService, zookeeperServer, host2, null);

        assertFalse(zookeeperSch.isRestartRequired()); //No restart required if adding host

        deleteServiceComponentHost(clusterName, zookeeperService, zookeeperServer, host2, null);
        deleteHost(host2);

        assertTrue(zookeeperSch.isRestartRequired()); //Restart if removing host!
    }

    @Test
    public void testMaintenanceState() throws Exception {
        String clusterName = "c1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-1.2.0"));

        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);

        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put("context", "Called from a test");

        Cluster cluster = clusters.getCluster(clusterName);
        Service service = cluster.getService(serviceName);
        Map<String, Host> hosts = clusters.getHostsForCluster(clusterName);

        MaintenanceStateHelper maintenanceStateHelper = MaintenanceStateHelperTest
                .getMaintenanceStateHelperInstance(clusters);

        // test updating a service
        ServiceRequest sr = new ServiceRequest(clusterName, serviceName, null);
        sr.setMaintenanceState(MaintenanceState.ON.name());
        ServiceResourceProviderTest.updateServices(controller, Collections.singleton(sr), requestProperties, false,
                false, maintenanceStateHelper);
        Assert.assertEquals(MaintenanceState.ON, service.getMaintenanceState());

        // check the host components implied state vs desired state
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(MaintenanceState.IMPLIED_FROM_SERVICE,
                        controller.getEffectiveMaintenanceState(sch));
                Assert.assertEquals(MaintenanceState.OFF, sch.getMaintenanceState());
            }
        }

        // reset
        sr.setMaintenanceState(MaintenanceState.OFF.name());
        ServiceResourceProviderTest.updateServices(controller, Collections.singleton(sr), requestProperties, false,
                false, maintenanceStateHelper);
        Assert.assertEquals(MaintenanceState.OFF, service.getMaintenanceState());

        // check the host components implied state vs desired state
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(MaintenanceState.OFF, controller.getEffectiveMaintenanceState(sch));
                Assert.assertEquals(MaintenanceState.OFF, sch.getMaintenanceState());
            }
        }

        // passivate a host
        HostRequest hr = new HostRequest(host1, clusterName, requestProperties);
        hr.setMaintenanceState(MaintenanceState.ON.name());
        HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr));

        Host host = hosts.get(host1);
        Assert.assertEquals(MaintenanceState.ON, host.getMaintenanceState(cluster.getClusterId()));

        // check the host components implied state vs desired state, only for
        // affected hosts
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                MaintenanceState implied = controller.getEffectiveMaintenanceState(sch);
                if (sch.getHostName().equals(host1)) {
                    Assert.assertEquals(MaintenanceState.IMPLIED_FROM_HOST, implied);
                } else {
                    Assert.assertEquals(MaintenanceState.OFF, implied);
                }
                Assert.assertEquals(MaintenanceState.OFF, sch.getMaintenanceState());
            }
        }

        // reset
        hr.setMaintenanceState(MaintenanceState.OFF.name());
        HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr));

        host = hosts.get(host1);
        Assert.assertEquals(MaintenanceState.OFF, host.getMaintenanceState(cluster.getClusterId()));

        // check the host components active state vs desired state
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(MaintenanceState.OFF, controller.getEffectiveMaintenanceState(sch));
                Assert.assertEquals(MaintenanceState.OFF, sch.getMaintenanceState());
            }
        }

        // passivate several hosts
        HostRequest hr1 = new HostRequest(host1, clusterName, requestProperties);
        hr1.setMaintenanceState(MaintenanceState.ON.name());
        HostRequest hr2 = new HostRequest(host2, clusterName, requestProperties);
        hr2.setMaintenanceState(MaintenanceState.ON.name());
        Set<HostRequest> set = new HashSet<HostRequest>();
        set.add(hr1);
        set.add(hr2);
        HostResourceProviderTest.updateHosts(controller, set);

        host = hosts.get(host1);
        Assert.assertEquals(MaintenanceState.ON, host.getMaintenanceState(cluster.getClusterId()));
        host = hosts.get(host2);
        Assert.assertEquals(MaintenanceState.ON, host.getMaintenanceState(cluster.getClusterId()));

        // reset
        hr1 = new HostRequest(host1, clusterName, requestProperties);
        hr1.setMaintenanceState(MaintenanceState.OFF.name());
        hr2 = new HostRequest(host2, clusterName, requestProperties);
        hr2.setMaintenanceState(MaintenanceState.OFF.name());
        set = new HashSet<HostRequest>();
        set.add(hr1);
        set.add(hr2);

        HostResourceProviderTest.updateHosts(controller, set);
        host = hosts.get(host1);
        Assert.assertEquals(MaintenanceState.OFF, host.getMaintenanceState(cluster.getClusterId()));
        host = hosts.get(host2);
        Assert.assertEquals(MaintenanceState.OFF, host.getMaintenanceState(cluster.getClusterId()));

        // only do one SCH
        ServiceComponentHost targetSch = service.getServiceComponent(componentName2).getServiceComponentHosts()
                .get(host2);
        Assert.assertNotNull(targetSch);
        targetSch.setMaintenanceState(MaintenanceState.ON);

        // check the host components active state vs desired state
        Assert.assertEquals(MaintenanceState.ON, controller.getEffectiveMaintenanceState(targetSch));

        // update the service
        service.setMaintenanceState(MaintenanceState.ON);
        Assert.assertEquals(MaintenanceState.ON, controller.getEffectiveMaintenanceState(targetSch));

        // make SCH active
        targetSch.setMaintenanceState(MaintenanceState.OFF);
        Assert.assertEquals(MaintenanceState.IMPLIED_FROM_SERVICE,
                controller.getEffectiveMaintenanceState(targetSch));

        // update the service
        service.setMaintenanceState(MaintenanceState.OFF);
        Assert.assertEquals(MaintenanceState.OFF, controller.getEffectiveMaintenanceState(targetSch));

        host = hosts.get(host2);
        // update host
        host.setMaintenanceState(cluster.getClusterId(), MaintenanceState.ON);
        Assert.assertEquals(MaintenanceState.IMPLIED_FROM_HOST, controller.getEffectiveMaintenanceState(targetSch));

        targetSch.setMaintenanceState(MaintenanceState.ON);
        Assert.assertEquals(MaintenanceState.ON, controller.getEffectiveMaintenanceState(targetSch));

        // check the host components active state vs desired state
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(State.INIT, sch.getState());
            }
        }

        long id1 = installService(clusterName, serviceName, false, false, maintenanceStateHelper);

        List<HostRoleCommand> hdfsCmds = actionDB.getRequestTasks(id1);
        Assert.assertNotNull(hdfsCmds);

        HostRoleCommand datanodeCmd = null;

        for (HostRoleCommand cmd : hdfsCmds) {
            if (cmd.getRole().equals(Role.DATANODE)) {
                datanodeCmd = cmd;
            }
        }

        Assert.assertNotNull(datanodeCmd);

        // verify passive sch was skipped
        for (ServiceComponent sc : service.getServiceComponents().values()) {
            if (!sc.getName().equals(componentName2)) {
                continue;
            }

            for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
                Assert.assertEquals(sch == targetSch ? State.INIT : State.INSTALLED, sch.getState());
            }
        }
    }

    @Test
    public void testPassiveSkipServices() throws Exception {
        String clusterName = "c1";
        createCluster(clusterName);
        clusters.getCluster(clusterName).setDesiredStackVersion(new StackId("HDP-0.1"));

        String serviceName1 = "HDFS";
        String serviceName2 = "MAPREDUCE";
        createService(clusterName, serviceName1, null);
        createService(clusterName, serviceName2, null);

        String componentName1_1 = "NAMENODE";
        String componentName1_2 = "DATANODE";
        String componentName1_3 = "HDFS_CLIENT";
        createServiceComponent(clusterName, serviceName1, componentName1_1, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName1_2, State.INIT);
        createServiceComponent(clusterName, serviceName1, componentName1_3, State.INIT);

        String componentName2_1 = "JOBTRACKER";
        String componentName2_2 = "TASKTRACKER";
        createServiceComponent(clusterName, serviceName2, componentName2_1, State.INIT);
        createServiceComponent(clusterName, serviceName2, componentName2_2, State.INIT);

        String host1 = "h1";
        String host2 = "h2";

        addHost(host1, clusterName);
        addHost(host2, clusterName);

        createServiceComponentHost(clusterName, serviceName1, componentName1_1, host1, null);
        createServiceComponentHost(clusterName, serviceName1, componentName1_2, host1, null);
        createServiceComponentHost(clusterName, serviceName1, componentName1_2, host2, null);

        createServiceComponentHost(clusterName, serviceName2, componentName2_1, host1, null);
        createServiceComponentHost(clusterName, serviceName2, componentName2_2, host2, null);

        MaintenanceStateHelper maintenanceStateHelper = MaintenanceStateHelperTest
                .getMaintenanceStateHelperInstance(clusters);

        installService(clusterName, serviceName1, false, false, maintenanceStateHelper);
        installService(clusterName, serviceName2, false, false, maintenanceStateHelper);

        startService(clusterName, serviceName1, false, false, maintenanceStateHelper);
        startService(clusterName, serviceName2, false, false, maintenanceStateHelper);

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put("context", "Called from a test");

        Cluster cluster = clusters.getCluster(clusterName);

        for (Service service : cluster.getServices().values()) {
            Assert.assertEquals(State.STARTED, service.getDesiredState());
        }

        Service service2 = cluster.getService(serviceName2);
        service2.setMaintenanceState(MaintenanceState.ON);

        Set<ServiceRequest> srs = new HashSet<ServiceRequest>();
        srs.add(new ServiceRequest(clusterName, serviceName1, State.INSTALLED.name()));
        srs.add(new ServiceRequest(clusterName, serviceName2, State.INSTALLED.name()));
        RequestStatusResponse rsr = ServiceResourceProviderTest.updateServices(controller, srs, requestProperties,
                false, false, maintenanceStateHelper);

        for (ShortTaskStatus sts : rsr.getTasks()) {
            String role = sts.getRole();
            Assert.assertFalse(role.equals(componentName2_1));
            Assert.assertFalse(role.equals(componentName2_2));
        }

        for (Service service : cluster.getServices().values()) {
            if (service.getName().equals(serviceName2)) {
                Assert.assertEquals(State.STARTED, service.getDesiredState());
            } else {
                Assert.assertEquals(State.INSTALLED, service.getDesiredState());
            }
        }

        service2.setMaintenanceState(MaintenanceState.OFF);
        ServiceResourceProviderTest.updateServices(controller, srs, requestProperties, false, false,
                maintenanceStateHelper);
        for (Service service : cluster.getServices().values()) {
            Assert.assertEquals(State.INSTALLED, service.getDesiredState());
        }

        startService(clusterName, serviceName1, false, false, maintenanceStateHelper);
        startService(clusterName, serviceName2, false, false, maintenanceStateHelper);

        // test host
        Host h1 = clusters.getHost(host1);
        h1.setMaintenanceState(cluster.getClusterId(), MaintenanceState.ON);

        srs = new HashSet<ServiceRequest>();
        srs.add(new ServiceRequest(clusterName, serviceName1, State.INSTALLED.name()));
        srs.add(new ServiceRequest(clusterName, serviceName2, State.INSTALLED.name()));

        rsr = ServiceResourceProviderTest.updateServices(controller, srs, requestProperties, false, false,
                maintenanceStateHelper);

        for (ShortTaskStatus sts : rsr.getTasks()) {
            Assert.assertFalse(sts.getHostName().equals(host1));
        }

        h1.setMaintenanceState(cluster.getClusterId(), MaintenanceState.OFF);
        startService(clusterName, serviceName2, false, false, maintenanceStateHelper);

        service2.setMaintenanceState(MaintenanceState.ON);

        ServiceRequest sr = new ServiceRequest(clusterName, serviceName2, State.INSTALLED.name());
        rsr = ServiceResourceProviderTest.updateServices(controller, Collections.singleton(sr), requestProperties,
                false, false, maintenanceStateHelper);

        Assert.assertTrue(
                "Service start request defaults to Cluster operation level," + "command does not create tasks",
                rsr == null || rsr.getTasks().size() == 0);

    }

    @Test
    public void testEmptyConfigs() throws Exception {
        String clusterName = "c1";
        createCluster(clusterName);
        Cluster cluster = clusters.getCluster(clusterName);
        cluster.setDesiredStackVersion(new StackId("HDP-0.1"));

        ClusterRequest cr = new ClusterRequest(cluster.getClusterId(), cluster.getClusterName(), null, null);

        // test null map with no prior
        cr.setDesiredConfig(
                Collections.singletonList(new ConfigurationRequest(clusterName, "typeA", "v1", null, null)));
        controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
        Config config = cluster.getDesiredConfigByType("typeA");
        Assert.assertNull(config);

        // test empty map with no prior
        cr.setDesiredConfig(Collections.singletonList(new ConfigurationRequest(clusterName, "typeA", "v1",
                new HashMap<String, String>(), new HashMap<String, Map<String, String>>())));
        controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
        config = cluster.getDesiredConfigByType("typeA");
        Assert.assertNotNull(config);

        // test empty properties on a new version
        cr.setDesiredConfig(Collections.singletonList(new ConfigurationRequest(clusterName, "typeA", "v2",
                new HashMap<String, String>(), new HashMap<String, Map<String, String>>())));
        controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
        config = cluster.getDesiredConfigByType("typeA");
        Assert.assertNotNull(config);
        Assert.assertEquals(Integer.valueOf(0), Integer.valueOf(config.getProperties().size()));

        // test new version
        Map<String, String> map = new HashMap<String, String>();
        map.clear();
        map.put("c", "d");
        Map<String, Map<String, String>> attributesMap = new HashMap<String, Map<String, String>>();
        attributesMap.put("final", new HashMap<String, String>());
        attributesMap.get("final").put("c", "true");
        cr.setDesiredConfig(Collections
                .singletonList(new ConfigurationRequest(clusterName, "typeA", "v3", map, attributesMap)));
        controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
        config = cluster.getDesiredConfigByType("typeA");
        Assert.assertNotNull(config);
        Assert.assertTrue(config.getProperties().containsKey("c"));

        // test reset to v2
        cr.setDesiredConfig(Collections.singletonList(new ConfigurationRequest(clusterName, "typeA", "v2",
                new HashMap<String, String>(), new HashMap<String, Map<String, String>>())));
        controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
        config = cluster.getDesiredConfigByType("typeA");
        Assert.assertEquals("v2", config.getTag());
        Assert.assertNotNull(config);
        Assert.assertEquals(Integer.valueOf(0), Integer.valueOf(config.getProperties().size()));

        // test v2, but with properties
        cr.setDesiredConfig(Collections
                .singletonList(new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>() {
                    {
                        put("a", "b");
                    }
                }, new HashMap<String, Map<String, String>>() {
                    {
                        put("final", new HashMap<String, String>() {
                            {
                                put("a", "true");
                            }
                        });
                    }
                })));
        try {
            controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
            Assert.fail("Expect failure when creating a config that exists");
        } catch (AmbariException e) {
            // expected
        }
    }

    @Test
    public void testCreateCustomActionNoCluster() throws Exception {
        String hostname1 = "h1";
        String hostname2 = "h2";
        addHost(hostname1);
        addHost(hostname2);

        ambariMetaInfo.addActionDefinition(new ActionDefinition("a1", ActionType.SYSTEM, "", "", "",
                "action def description", TargetHostType.ANY, Short.valueOf("60")));

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put(REQUEST_CONTEXT_PROPERTY, "Called from a test");

        Map<String, String> requestParams = new HashMap<String, String>();
        requestParams.put("some_custom_param", "abc");

        // !!! target single host
        List<String> hosts = Arrays.asList(hostname1);
        RequestResourceFilter resourceFilter = new RequestResourceFilter(null, null, hosts);
        List<RequestResourceFilter> resourceFilters = new ArrayList<RequestResourceFilter>();
        resourceFilters.add(resourceFilter);

        ExecuteActionRequest actionRequest = new ExecuteActionRequest(null, null, "a1", resourceFilters, null,
                requestParams, false);
        RequestStatusResponse response = controller.createAction(actionRequest, requestProperties);
        assertEquals(1, response.getTasks().size());
        ShortTaskStatus taskStatus = response.getTasks().get(0);
        Assert.assertEquals(hostname1, taskStatus.getHostName());

        Stage stage = actionDB.getAllStages(response.getRequestId()).get(0);
        Assert.assertNotNull(stage);
        Assert.assertEquals(-1L, stage.getClusterId());

        List<HostRoleCommand> storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Assert.assertEquals(1, storedTasks.size());
        HostRoleCommand task = storedTasks.get(0);
        Assert.assertEquals(RoleCommand.ACTIONEXECUTE, task.getRoleCommand());
        Assert.assertEquals("a1", task.getRole().name());
        Assert.assertEquals(hostname1, task.getHostName());

        ExecutionCommand cmd = task.getExecutionCommandWrapper().getExecutionCommand();
        Type type = new TypeToken<Map<String, String>>() {
        }.getType();
        Map<String, String> commandParamsStage = StageUtils.getGson().fromJson(stage.getCommandParamsStage(), type);
        Assert.assertTrue(commandParamsStage.containsKey("some_custom_param"));
        Assert.assertEquals(null, cmd.getServiceName());
        Assert.assertEquals(null, cmd.getComponentName());

        // !!! target two hosts

        hosts = Arrays.asList(hostname1, hostname2);
        resourceFilter = new RequestResourceFilter(null, null, hosts);
        resourceFilters = new ArrayList<RequestResourceFilter>();
        resourceFilters.add(resourceFilter);

        actionRequest = new ExecuteActionRequest(null, null, "a1", resourceFilters, null, requestParams, false);
        response = controller.createAction(actionRequest, requestProperties);
        assertEquals(2, response.getTasks().size());
        boolean host1Found = false;
        boolean host2Found = false;
        for (ShortTaskStatus sts : response.getTasks()) {
            if (sts.getHostName().equals(hostname1)) {
                host1Found = true;
            } else if (sts.getHostName().equals(hostname2)) {
                host2Found = true;
            }
        }
        Assert.assertTrue(host1Found);
        Assert.assertTrue(host2Found);

        stage = actionDB.getAllStages(response.getRequestId()).get(0);
        Assert.assertNotNull(stage);
        Assert.assertEquals(-1L, stage.getClusterId());

        storedTasks = actionDB.getRequestTasks(response.getRequestId());
        Assert.assertEquals(2, storedTasks.size());
        task = storedTasks.get(0);
        Assert.assertEquals(RoleCommand.ACTIONEXECUTE, task.getRoleCommand());
        Assert.assertEquals("a1", task.getRole().name());
        Assert.assertEquals(hostname1, task.getHostName());

        cmd = task.getExecutionCommandWrapper().getExecutionCommand();
        commandParamsStage = StageUtils.getGson().fromJson(stage.getCommandParamsStage(), type);
        Assert.assertTrue(commandParamsStage.containsKey("some_custom_param"));
        Assert.assertEquals(null, cmd.getServiceName());
        Assert.assertEquals(null, cmd.getComponentName());
    }

    @Test
    public void testConfigAttributesStaleConfigFilter() throws AmbariException {

        final String host1 = "h1";
        final String host2 = "h2";
        Long clusterId = 1L;
        String clusterName = "foo1";
        setupClusterWithHosts(clusterName, "HDP-2.0.5", new ArrayList<String>() {
            {
                add(host1);
                add(host2);
            }
        }, "centos5");
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";
        String componentName2 = "DATANODE";
        String componentName3 = "HDFS_CLIENT";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
        createServiceComponent(clusterName, serviceName, componentName3, State.INIT);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
        createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
        createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create and attach config
        // hdfs-site will not have config-attributes
        Map<String, String> hdfsConfigs = new HashMap<String, String>();
        hdfsConfigs.put("a", "b");
        Map<String, Map<String, String>> hdfsConfigAttributes = new HashMap<String, Map<String, String>>() {
            {
                put("final", new HashMap<String, String>() {
                    {
                        put("a", "true");
                    }
                });
            }
        };

        ConfigurationRequest cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", hdfsConfigs,
                hdfsConfigAttributes);
        ClusterRequest crReq1 = new ClusterRequest(clusterId, clusterName, null, null);
        crReq1.setDesiredConfig(Collections.singletonList(cr1));

        controller.updateClusters(Collections.singleton(crReq1), null);

        // Start
        startService(clusterName, serviceName, false, false);

        // Update actual config
        HashMap<String, Map<String, String>> actualConfig = new HashMap<String, Map<String, String>>() {
            {
                put("hdfs-site", new HashMap<String, String>() {
                    {
                        put("tag", "version1");
                    }
                });
            }
        };
        HashMap<String, Map<String, String>> actualConfigOld = new HashMap<String, Map<String, String>>() {
            {
                put("hdfs-site", new HashMap<String, String>() {
                    {
                        put("tag", "version0");
                    }
                });
            }
        };

        Service s1 = clusters.getCluster(clusterName).getService(serviceName);
        s1.getServiceComponent(componentName1).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
        s1.getServiceComponent(componentName3).getServiceComponentHost(host1).updateActualConfigs(actualConfigOld);
        s1.getServiceComponent(componentName2).getServiceComponentHost(host2).updateActualConfigs(actualConfigOld);
        s1.getServiceComponent(componentName3).getServiceComponentHost(host2).updateActualConfigs(actualConfig);

        ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(5, resps.size());

        // Get all host components with stale config = true
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setStaleConfig("true");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        // Get all host components with stale config = false
        r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
        r.setStaleConfig("false");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(3, resps.size());

        // Get all host components with stale config = false and hostname filter
        r = new ServiceComponentHostRequest(clusterName, null, null, host1, null);
        r.setStaleConfig("false");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(2, resps.size());

        // Get all host components with stale config = false and hostname filter
        r = new ServiceComponentHostRequest(clusterName, null, null, host2, null);
        r.setStaleConfig("true");
        resps = controller.getHostComponents(Collections.singleton(r));
        Assert.assertEquals(1, resps.size());
    }

    @Test
    public void testTargetedProcessCommand() throws Exception {
        final String host1 = "h1";
        String clusterName = "c1";
        Cluster cluster = setupClusterWithHosts(clusterName, "HDP-2.0.5", Arrays.asList(host1), "centos5");
        String serviceName = "HDFS";
        createService(clusterName, serviceName, null);
        String componentName1 = "NAMENODE";

        createServiceComponent(clusterName, serviceName, componentName1, State.INIT);

        createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);

        // Install
        installService(clusterName, serviceName, false, false);

        // Create and attach config
        // hdfs-site will not have config-attributes
        Map<String, String> hdfsConfigs = new HashMap<String, String>();
        hdfsConfigs.put("a", "b");
        Map<String, Map<String, String>> hdfsConfigAttributes = new HashMap<String, Map<String, String>>() {
            {
                put("final", new HashMap<String, String>() {
                    {
                        put("a", "true");
                    }
                });
            }
        };

        ConfigurationRequest cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", hdfsConfigs,
                hdfsConfigAttributes);
        ClusterRequest crReq1 = new ClusterRequest(cluster.getClusterId(), clusterName, null, null);
        crReq1.setDesiredConfig(Collections.singletonList(cr1));

        controller.updateClusters(Collections.singleton(crReq1), null);

        // Start
        startService(clusterName, serviceName, false, false);

        ServiceComponentHostRequest req = new ServiceComponentHostRequest(clusterName, serviceName, componentName1,
                host1, "INSTALLED");

        Map<String, String> requestProperties = new HashMap<String, String>();
        requestProperties.put("namenode", "p1");
        RequestStatusResponse resp = updateHostComponents(Collections.singleton(req), requestProperties, false);

        // succeed in creating a task
        assertNotNull(resp);

        // manually change live state to stopped as no running action manager
        for (ServiceComponentHost sch : clusters.getCluster(clusterName).getServiceComponentHosts(host1)) {
            sch.setState(State.INSTALLED);
        }

        // no new commands since no targeted info
        resp = updateHostComponents(Collections.singleton(req), new HashMap<String, String>(), false);
        assertNull(resp);

        // role commands added for targeted command
        resp = updateHostComponents(Collections.singleton(req), requestProperties, false);
        assertNotNull(resp);

    }

    @Test
    public void testGetPackagesForServiceHost() throws Exception {
        ServiceInfo service = ambariMetaInfo.getStack("HDP", "2.0.1").getService("HIVE");
        HashMap<String, String> hostParams = new HashMap<String, String>();

        Map<String, ServiceOsSpecific.Package> packages = new HashMap<String, ServiceOsSpecific.Package>();
        String[] packageNames = { "hive", "mysql-connector-java", "mysql", "mysql-server", "mysql-client" };
        for (String packageName : packageNames) {
            ServiceOsSpecific.Package pkg = new ServiceOsSpecific.Package();
            pkg.setName(packageName);
            packages.put(packageName, pkg);
        }

        List<ServiceOsSpecific.Package> rhel5Packages = controller.getPackagesForServiceHost(service, hostParams,
                "redhat5");
        List<ServiceOsSpecific.Package> expectedRhel5 = Arrays.asList(packages.get("hive"),
                packages.get("mysql-connector-java"), packages.get("mysql"), packages.get("mysql-server"));

        List<ServiceOsSpecific.Package> sles11Packages = controller.getPackagesForServiceHost(service, hostParams,
                "suse11");
        List<ServiceOsSpecific.Package> expectedSles11 = Arrays.asList(packages.get("hive"),
                packages.get("mysql-connector-java"), packages.get("mysql"), packages.get("mysql-client"));
        assertThat(rhel5Packages, is(expectedRhel5));
        assertThat(sles11Packages, is(expectedSles11));
    }

    // this is a temporary measure as a result of moving updateHostComponents from AmbariManagementController
    // to HostComponentResourceProvider.  Eventually the tests should be moved out of this class.
    private RequestStatusResponse updateHostComponents(Set<ServiceComponentHostRequest> requests,
            Map<String, String> requestProperties, boolean runSmokeTest) throws Exception {

        return updateHostComponents(controller, requests, requestProperties, runSmokeTest);
    }

    private RequestStatusResponse updateHostComponents(AmbariManagementController controller,
            Set<ServiceComponentHostRequest> requests, Map<String, String> requestProperties, boolean runSmokeTest)
            throws Exception {

        return HostComponentResourceProviderTest.updateHostComponents(controller, injector, requests,
                requestProperties, runSmokeTest);
    }

}