com.vmware.bdd.plugin.clouderamgr.service.CmClusterValidator.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.bdd.plugin.clouderamgr.service.CmClusterValidator.java

Source

/***************************************************************************
 * Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
 * Licensed 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 com.vmware.bdd.plugin.clouderamgr.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gson.Gson;
import com.vmware.bdd.plugin.clouderamgr.model.support.AvailableServiceRole;
import com.vmware.bdd.plugin.clouderamgr.model.support.AvailableServiceRoleContainer;
import com.vmware.bdd.plugin.clouderamgr.utils.CmUtils;
import com.vmware.bdd.software.mgmt.plugin.exception.ValidationException;
import com.vmware.bdd.software.mgmt.plugin.model.ClusterBlueprint;
import com.vmware.bdd.software.mgmt.plugin.model.NodeGroupInfo;
import com.vmware.bdd.usermgmt.UserMgmtConstants;
import com.vmware.bdd.utils.Constants;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * Author: Xiaoding Bian
 * Date: 7/11/14
 * Time: 1:37 PM
 */
public class CmClusterValidator {

    private static final Logger logger = Logger.getLogger(CmClusterValidator.class);
    private List<String> warningMsgList;
    private List<String> errorMsgList;

    public CmClusterValidator() {
        this.warningMsgList = new ArrayList<String>();
        this.errorMsgList = new ArrayList<String>();
    }

    public boolean validateBlueprint(ClusterBlueprint blueprint) {
        logger.info("Start to validate blueprint for cluster " + blueprint.getName());

        String distro = blueprint.getHadoopStack().getDistro();
        String distroVersion = CmUtils.distroVersionOfHadoopStack(blueprint.getHadoopStack());

        try {
            List<String> unRecogConfigTypes = new ArrayList<String>();
            List<String> unRecogConfigKeys = new ArrayList<>();
            validateConfigs(blueprint.getConfiguration(), unRecogConfigTypes, unRecogConfigKeys, distroVersion);

            Set<String> availableRoles = AvailableServiceRoleContainer.allRoles(distroVersion);

            Set<String> definedServices = new HashSet<String>();
            Map<String, Integer> definedRoles = new HashMap<String, Integer>();

            List<String> unRecogRoles = null;
            Set<String> invalidRacks = null;

            if (blueprint.getNodeGroups() == null || blueprint.getNodeGroups().isEmpty()) {
                return false;
            }

            int nnGroupsNum = 0;
            for (NodeGroupInfo group : blueprint.getNodeGroups()) {
                validateConfigs(group.getConfiguration(), unRecogConfigTypes, unRecogConfigKeys, distroVersion);
                if (group.getRoles().contains("HDFS_NAMENODE")) {
                    nnGroupsNum++;
                }

                for (String roleName : group.getRoles()) {
                    if (!availableRoles.contains(roleName)) {
                        if (unRecogRoles == null) {
                            unRecogRoles = new ArrayList<String>();
                        }
                        unRecogRoles.add(roleName);
                    } else {
                        if (!definedRoles.containsKey(roleName)) {
                            definedRoles.put(roleName, group.getInstanceNum());
                        } else {
                            Integer instanceNum = definedRoles.get(roleName) + group.getInstanceNum();
                            definedRoles.put(roleName, instanceNum);
                        }
                        definedServices
                                .add(AvailableServiceRoleContainer.load(roleName).getParent().getDisplayName());
                    }
                }
            }

            if (nnGroupsNum > 1) {
                errorMsgList.add("Namenode federation is not supported currently");
            }

            if (unRecogRoles != null && !unRecogRoles.isEmpty()) { // point 1: unrecognized roles
                errorMsgList.add("Roles " + unRecogRoles.toString() + " are not available by distro " + distro);
            }

            if (!unRecogConfigTypes.isEmpty()) { // point 2: add to warning list as will be ignored by creating logic
                warningMsgList.add("Configurations for " + unRecogConfigTypes.toString()
                        + " are not available by distro " + distro);
            }

            if (!unRecogConfigKeys.isEmpty()) { // point 3
                errorMsgList.add("Configuration items " + unRecogConfigKeys.toString() + " are invalid");
            }

            if (invalidRacks != null && !invalidRacks.isEmpty()) {
                errorMsgList.add("Racks " + invalidRacks.toString() + " are invalid,"
                        + " rack names must be slash-separated, like Unix paths. For example, \"/rack1\" and \"/cabinet3/rack4\"");
            }

            for (String serviceName : definedServices) {
                // service dependency check
                for (AvailableServiceRole.Dependency dependency : AvailableServiceRoleContainer.load(serviceName)
                        .getDependencies()) {
                    if (!dependency.isRequired()) {
                        continue;
                    }
                    if (dependency.getServices().size() == 1
                            && !definedServices.contains(dependency.getServices().get(0))) {
                        if (serviceName.equals("YARN") && isComputeOnly(definedServices)) {
                            continue;
                        }
                        warningMsgList
                                .add(serviceName + " depends on " + dependency.getServices().get(0) + " service");
                    } else {
                        boolean found = false;
                        for (String dependService : dependency.getServices()) {
                            if (definedServices.contains(dependService)) {
                                found = true;
                            }
                        }
                        if (!found) {
                            warningMsgList.add(serviceName + " depends on one service of "
                                    + dependency.getServices().toString());
                        }
                    }
                }

                Set<String> requiredRoles = new HashSet<String>();
                switch (serviceName) {
                case "HDFS":
                    if (!isComputeOnly(definedServices)) {
                        requiredRoles.add("HDFS_NAMENODE");
                        requiredRoles.add("HDFS_DATANODE");
                    }
                    if (checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList)) {
                        if (nnGroupsNum == 1) {
                            if (definedRoles.get("HDFS_NAMENODE") < 2
                                    && !definedRoles.containsKey("HDFS_SECONDARY_NAMENODE")) {
                                errorMsgList.add(
                                        "HDFS service not configured for High Availability must have a SecondaryNameNode");
                            }
                            if (definedRoles.get("HDFS_NAMENODE") >= 2
                                    && !definedRoles.containsKey("HDFS_JOURNALNODE")) {
                                errorMsgList.add(
                                        "HDFS service configured for High Availability must have journal nodes");
                            }
                        }
                    }
                    if (definedRoles.containsKey("HDFS_JOURNALNODE")) {
                        if (definedRoles.get("HDFS_JOURNALNODE") > 1 && definedRoles.get("HDFS_JOURNALNODE") < 3) {
                            errorMsgList.add(Constants.WRONG_NUM_OF_JOURNALNODE);
                        } else if (definedRoles.get("HDFS_JOURNALNODE") % 2 == 0) {
                            warningMsgList.add(Constants.ODD_NUM_OF_JOURNALNODE);
                        }
                    }
                    break;
                case "YARN":
                    requiredRoles.add("YARN_RESOURCE_MANAGER");
                    requiredRoles.add("YARN_NODE_MANAGER");
                    requiredRoles.add("YARN_JOB_HISTORY");
                    if (checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList)) {
                        if (definedRoles.get("YARN_RESOURCE_MANAGER") > 1) {
                            errorMsgList.add(Constants.WRONG_NUM_OF_RESOURCEMANAGER);
                        }
                    }
                    break;
                case "MAPREDUCE":
                    requiredRoles.add("MAPREDUCE_JOBTRACKER");
                    requiredRoles.add("MAPREDUCE_TASKTRACKER");
                    if (checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList)) {
                        if (definedRoles.get("MAPREDUCE_JOBTRACKER") > 1) {
                            errorMsgList.add(Constants.WRONG_NUM_OF_JOBTRACKER);
                        }
                    }
                    break;
                case "HBASE":
                    requiredRoles.add("HBASE_MASTER");
                    requiredRoles.add("HBASE_REGION_SERVER");
                    checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList);
                    break;
                case "ZOOKEEPER":
                    requiredRoles.add("ZOOKEEPER_SERVER");
                    if (checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList)) {
                        if (definedRoles.get("ZOOKEEPER_SERVER") > 0 && definedRoles.get("ZOOKEEPER_SERVER") < 3) {
                            errorMsgList.add(Constants.WRONG_NUM_OF_ZOOKEEPER);
                        } else if (definedRoles.get("ZOOKEEPER_SERVER") % 2 == 0) {
                            warningMsgList.add(Constants.ODD_NUM_OF_ZOOKEEPER);
                        }
                    }
                    break;
                case "HIVE":
                    requiredRoles.add("HIVE_METASTORE");
                    requiredRoles.add("HIVE_SERVER2");
                    checkRequiredRoles(serviceName, requiredRoles, definedRoles.keySet(), errorMsgList);
                    String[] requiredConfigs = { "hive_metastore_database_host", "hive_metastore_database_name",
                            "hive_metastore_database_password", "hive_metastore_database_port",
                            "hive_metastore_database_type", "hive_metastore_database_user" };

                    boolean configured = true;
                    if (blueprint.getConfiguration().containsKey("HIVE")) {
                        Map<String, String> configuredItems = (Map<String, String>) blueprint.getConfiguration()
                                .get("HIVE");
                        for (String item : requiredConfigs) {
                            if (!configuredItems.containsKey(item)) {
                                configured = false;
                                break;
                            }
                        }
                    } else {
                        configured = false;
                    }

                    if (!configured) {
                        errorMsgList.add(
                                "HIVE service depends on an external database, please setup one and provide configuration properties ["
                                        + StringUtils.join(requiredConfigs, ",") + "] for HIVE service");
                    }

                    break;
                case "OOZIE":
                    if (definedRoles.get("OOZIE_SERVER") > 1) {
                        errorMsgList.add("only one OOZIE_SERVER is allowed for OOZIE service");
                    }
                    break;
                case "SENTRY":
                    if (definedRoles.get("SENTRY_SERVER") > 1) {
                        errorMsgList.add("only one SENTRY_SERVER is allowed for SENTRY service");
                    }
                    break;
                case "SQOOP":
                    if (definedRoles.get("SQOOP_SERVER") > 1) {
                        errorMsgList.add("only one SQOOP_SERVER is allowed for SQOOP service");
                    }
                    break;
                case "ISILON":
                    requiredRoles.add("YARN_RESOURCE_MANAGER");
                    requiredRoles.add("YARN_JOB_HISTORY");
                    requiredRoles.add("YARN_NODE_MANAGER");
                    requiredRoles.add("GATEWAY");
                    break;
                default:
                    break;
                }

            }

        } catch (IOException e) {
            // IO exception ignored
        }

        if (!warningMsgList.isEmpty() || !errorMsgList.isEmpty()) {
            throw ValidationException.VALIDATION_FAIL("Blueprint", errorMsgList, warningMsgList);
        }

        return true;
    }

    private boolean checkRequiredRoles(String serviceName, Set<String> requiredRoles, Set<String> definedRoles,
            List<String> errorMsgList) {
        requiredRoles.removeAll(definedRoles);
        if (!requiredRoles.isEmpty()) {
            errorMsgList.add("Service " + serviceName + " requires roles " + requiredRoles.toString());
            return false;
        }
        return true;
    }

    private void validateConfigs(Map<String, Object> config, List<String> unRecogConfigTypes,
            List<String> unRecogConfigKeys, String distroVersion) {
        if (config == null || config.isEmpty()) {
            return;
        }

        for (String key : config.keySet()) {
            try {
                if (key.equals(UserMgmtConstants.SERVICE_USER_CONFIG_IN_SPEC_FILE)) {
                    Map<String, Map<String, String>> configs = (Map<String, Map<String, String>>) config
                            .get(UserMgmtConstants.SERVICE_USER_CONFIG_IN_SPEC_FILE);
                    List<Map<String, String>> configList = new ArrayList<>(configs.values());
                    validateServiceUserConfigs(configList, errorMsgList);
                    continue;
                }
                AvailableServiceRole def = AvailableServiceRoleContainer.load(key);
                if (!AvailableServiceRoleContainer.isSupported(distroVersion, def)) {
                    unRecogConfigTypes.add(key);
                    continue;
                }

                Map<String, String> items = (Map<String, String>) config.get(key);
                for (String subKey : items.keySet()) {
                    if (!def.getAvailableConfigurations().containsKey(subKey)) {
                        unRecogConfigKeys.add(subKey);
                    }
                }
            } catch (IOException e) {
                unRecogConfigTypes.add(key);
            }
        }
    }

    private void validateServiceUserConfigs(List<Map<String, String>> configs, List<String> errorMsgList) {
        for (Map<String, String> config : configs) {
            for (String key : config.keySet()) {
                if (!key.equalsIgnoreCase(UserMgmtConstants.SERVICE_USER_GROUP)
                        && !key.equalsIgnoreCase(UserMgmtConstants.SERVICE_USER_NAME)
                        && !key.equalsIgnoreCase(UserMgmtConstants.SERVICE_USER_TYPE)) {
                    errorMsgList.add("Service user config doesn't support key: " + key);
                }
                if (key.equalsIgnoreCase(UserMgmtConstants.SERVICE_USER_TYPE)
                        && !config.get(key).equalsIgnoreCase("LDAP")) {
                    errorMsgList.add(
                            "You must use LDAP user to customize service user in Cloudera manager deployed cluster");
                }
            }
        }
    }

    @Override
    public String toString() {
        Map<String, List<String>> message = new HashMap<String, List<String>>();
        message.put("WarningMsgList", this.warningMsgList);
        message.put("ErrorMsgList", this.errorMsgList);
        return (new Gson()).toJson(message);
    }

    private boolean isComputeOnly(Set<String> definedServices) {
        boolean isComputeOnly = false;
        if (definedServices.contains("ISILON")) {
            isComputeOnly = true;
        }
        return isComputeOnly;
    }
}