This is an automated email from the ASF dual-hosted git repository.

pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 248f08c  KNOX-1875 - Cloudera Manager service discovery
248f08c is described below

commit 248f08c7537cf1e9b631b768f18a7dbb6b7e5cbd
Author: pzampino <[email protected]>
AuthorDate: Fri Jan 18 12:02:56 2019 -0500

    KNOX-1875 - Cloudera Manager service discovery
---
 gateway-discovery-cm/pom.xml                       |  64 +++
 .../discovery/cm/ClouderaManagerCluster.java       | 100 +++++
 .../cm/ClouderaManagerServiceDiscovery.java        | 430 +++++++++++++++++++++
 .../ClouderaManagerServiceDiscoveryMessages.java   |  97 +++++
 .../cm/ClouderaManagerServiceDiscoveryType.java    |  37 ++
 .../topology/discovery/cm/DiscoveryApiClient.java  | 104 +++++
 .../topology/discovery/cm/ServiceModel.java        |  73 ++++
 .../discovery/cm/ServiceModelGenerator.java        |  39 ++
 .../cm/model/AbstractServiceModelGenerator.java    |  76 ++++
 .../model/atlas/AtlasAPIServiceModelGenerator.java |  39 ++
 .../cm/model/atlas/AtlasServiceModelGenerator.java |  60 +++
 .../model/atlas/AtlasUIServiceModelGenerator.java  |  39 ++
 .../model/hbase/HBaseUIServiceModelGenerator.java  |  58 +++
 .../model/hbase/WebHBaseServiceModelGenerator.java |  58 +++
 .../cm/model/hdfs/HdfsUIServiceModelGenerator.java |  51 +++
 .../model/hdfs/NameNodeServiceModelGenerator.java  |  60 +++
 .../model/hdfs/WebHdfsServiceModelGenerator.java   |  49 +++
 .../cm/model/hive/HiveServiceModelGenerator.java   |  65 ++++
 .../cm/model/hue/HueLBServiceModelGenerator.java   |  52 +++
 .../cm/model/hue/HueServiceModelGenerator.java     |  58 +++
 .../cm/model/livy/LivyServiceModelGenerator.java   |  58 +++
 .../cm/model/livy/LivyUIServiceModelGenerator.java |  39 ++
 .../cm/model/oozie/OozieServiceModelGenerator.java |  60 +++
 .../model/oozie/OozieUIServiceModelGenerator.java  |  39 ++
 .../model/ranger/RangerServiceModelGenerator.java  |  59 +++
 .../ranger/RangerUIAPIServiceModelGenerator.java   |  38 ++
 .../ranger/RangerUIServiceModelGenerator.java      |  38 ++
 .../cm/model/solr/SolrServiceModelGenerator.java   |  59 +++
 .../cm/model/solr/SolrUIServiceModelGenerator.java |  38 ++
 .../spark/SparkHistoryUIServiceModelGenerator.java |  59 +++
 .../yarn/JobHistoryUIServiceModelGenerator.java    |  70 ++++
 .../ResourceManagerUIServiceModelGenerator.java    |  40 ++
 .../cm/model/yarn/YarnUIServiceModelGenerator.java |  70 ++++
 .../model/yarn/YarnUIv2ServiceModelGenerator.java  |  38 ++
 .../zeppelin/ZeppelinUIServiceModelGenerator.java  |  57 +++
 .../zeppelin/ZeppelinWSServiceModelGenerator.java  |  49 +++
 ...gateway.topology.discovery.ServiceDiscoveryType |  19 +
 ...way.topology.discovery.cm.ServiceModelGenerator |  44 +++
 .../cm/ClouderaManagerServiceDiscoveryTest.java    | 283 ++++++++++++++
 gateway-release/pom.xml                            |   4 +
 pom.xml                                            |   6 +
 41 files changed, 2776 insertions(+)

diff --git a/gateway-discovery-cm/pom.xml b/gateway-discovery-cm/pom.xml
new file mode 100644
index 0000000..e646c68
--- /dev/null
+++ b/gateway-discovery-cm/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+    <parent>
+        <groupId>org.apache.knox</groupId>
+        <artifactId>gateway</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <properties>
+        <cloudera-manager.version>6.2.0</cloudera-manager.version>
+        
<cloudera-manager-api-swagger.version>${cloudera-manager.version}</cloudera-manager-api-swagger.version>
+    </properties>
+
+    <artifactId>gateway-discovery-cm</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-i18n</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-util-configinjector</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.cloudera.api.swagger</groupId>
+            <artifactId>cloudera-manager-api-swagger</artifactId>
+            <version>${cloudera-manager-api-swagger.version}</version>  <!-- 
or CM version 6.0 and above -->
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp</groupId>
+            <artifactId>okhttp</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
+
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
new file mode 100644
index 0000000..66e487b
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
@@ -0,0 +1,100 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ClouderaManager-based service discovery cluster model implementation.
+ */
+public class ClouderaManagerCluster implements ServiceDiscovery.Cluster {
+
+  private String name;
+
+  private Set<ServiceModel> serviceModels = new HashSet<>();
+
+  ClouderaManagerCluster(String clusterName) {
+    this.name = clusterName;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public List<String> getServiceURLs(String serviceName) {
+    List<String> urls = new ArrayList<>();
+
+    for (ServiceModel sm : serviceModels) {
+      if (sm.getService().equals(serviceName)) {
+        urls.add(sm.getServiceUrl());
+      }
+    }
+
+    return urls;
+  }
+
+  @Override
+  public List<String> getServiceURLs(String serviceName, Map<String, String> 
serviceParams) {
+    return getServiceURLs(serviceName); // TODO: PJZ: Support things like HDFS 
nameservice params for providing the correct URL(s)?
+  }
+
+  @Override
+  public ZooKeeperConfig getZooKeeperConfiguration(String serviceName) {
+    return null;
+  }
+
+  void addServiceModels(Set<ServiceModel> serviceModels) {
+    this.serviceModels.addAll(serviceModels);
+  }
+
+  static class ServiceConfiguration {
+
+    private String type;
+    private String version;
+    private Map<String, String> props;
+
+    ServiceConfiguration(String type, String version, Map<String, String> 
properties) {
+      this.type = type;
+      this.version = version;
+      this.props = properties;
+    }
+
+    public String getVersion() {
+      return version;
+    }
+
+    public String getType() {
+      return type;
+    }
+
+    public Map<String, String> getProperties() {
+      return props;
+    }
+  }
+
+
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
new file mode 100644
index 0000000..948f9d3
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscovery.java
@@ -0,0 +1,430 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import com.cloudera.api.swagger.ClustersResourceApi;
+import com.cloudera.api.swagger.RolesResourceApi;
+import com.cloudera.api.swagger.ServicesResourceApi;
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiCluster;
+import com.cloudera.api.swagger.model.ApiClusterList;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiRoleList;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import com.cloudera.api.swagger.model.ApiServiceList;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.security.SubjectUtils;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.topology.discovery.GatewayService;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.net.URI;
+import java.net.URL;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+
+/**
+ * ClouderaManager-based service discovery implementation.
+ */
+public class ClouderaManagerServiceDiscovery implements ServiceDiscovery {
+
+  static final String TYPE = "ClouderaManager";
+
+  private static final ClouderaManagerServiceDiscoveryMessages log =
+                                        
MessagesFactory.get(ClouderaManagerServiceDiscoveryMessages.class);
+
+  private static final String JGSS_LOGIN_MODULE = 
"com.sun.security.jgss.initiate";
+
+  static final String API_PATH = "api/v32";
+
+  private static final String CLUSTER_TYPE_ANY = "any";
+  private static final String VIEW_SUMMARY     = "summary";
+  private static final String VIEW_FULL        = "full";
+
+  static final String DEFAULT_USER_ALIAS = "cm.discovery.user";
+  static final String DEFAULT_PWD_ALIAS  = "cm.discovery.password";
+
+  private boolean debug;
+
+  @GatewayService
+  private AliasService aliasService;
+
+
+  ClouderaManagerServiceDiscovery() {
+    this(false);
+  }
+
+  ClouderaManagerServiceDiscovery(boolean debug) {
+    this.debug = debug;
+  }
+
+  @Override
+  public String getType() {
+    return TYPE;
+  }
+
+  private DiscoveryApiClient getClient(ServiceDiscoveryConfig discoveryConfig) 
{
+    String discoveryAddress = discoveryConfig.getAddress();
+    if (discoveryAddress == null || discoveryAddress.isEmpty()) {
+      log.missingDiscoveryAddress();
+      throw new IllegalArgumentException("Missing or invalid discovery 
address.");
+    }
+
+    DiscoveryApiClient client = new DiscoveryApiClient(discoveryConfig, 
aliasService);
+    client.setDebugging(debug);
+    client.setVerifyingSsl(false);
+    return client;
+  }
+
+  @Override
+  public Map<String, Cluster> discover(GatewayConfig gatewayConfig, 
ServiceDiscoveryConfig discoveryConfig) {
+    Map<String, Cluster> clusters = new HashMap<>();
+
+    DiscoveryApiClient client = getClient(discoveryConfig);
+    List<ApiCluster> apiClusters = getClusters(client);
+    for (ApiCluster apiCluster : apiClusters) {
+      String clusterName = apiCluster.getName();
+      log.discoveredCluster(clusterName, apiCluster.getFullVersion());
+
+      Cluster cluster = discover(gatewayConfig, discoveryConfig, clusterName, 
client);
+      clusters.put(clusterName, cluster);
+    }
+
+    return clusters;
+  }
+
+  @Override
+  public Cluster discover(GatewayConfig gatewayConfig, ServiceDiscoveryConfig 
discoveryConfig, String clusterName) {
+    return discover(gatewayConfig, discoveryConfig, clusterName, 
getClient(discoveryConfig));
+  }
+
+  protected Cluster discover(GatewayConfig          gatewayConfig,
+                             ServiceDiscoveryConfig discoveryConfig,
+                             String                 clusterName,
+                             DiscoveryApiClient     client) {
+    ServiceDiscovery.Cluster cluster = null;
+
+    if (clusterName == null || clusterName.isEmpty()) {
+      log.missingDiscoveryCluster();
+      throw new IllegalArgumentException("The cluster configuration is missing 
from, or invalid in, the discovery configuration.");
+    }
+
+    try {
+      cluster = discoverCluster(client, clusterName);
+    } catch (ApiException e) {
+      log.clusterDiscoveryError(clusterName, e);
+    }
+
+    return cluster;
+  }
+
+  private static List<ApiCluster> getClusters(DiscoveryApiClient client) {
+    List<ApiCluster> clusters = new ArrayList<>();
+    try {
+      ApiClusterList clusterList = null;
+
+      ClustersResourceApi clustersResourceApi = new 
ClustersResourceApi(client);
+      if (client.isKerberos()) {
+        clusterList =
+            Subject.doAs(getSubject(), (PrivilegedAction<ApiClusterList>) () 
-> {
+              try {
+                return clustersResourceApi.readClusters(CLUSTER_TYPE_ANY, 
VIEW_SUMMARY);
+              } catch (Exception e) {
+                log.clusterDiscoveryError(CLUSTER_TYPE_ANY, e);
+              }
+              return null;
+            });
+      } else {
+          clusterList = clustersResourceApi.readClusters(CLUSTER_TYPE_ANY, 
VIEW_SUMMARY);
+      }
+
+      if (clusterList != null) {
+        clusters.addAll(clusterList.getItems());
+      }
+    } catch (Exception e) {
+      log.clusterDiscoveryError(CLUSTER_TYPE_ANY, e); // TODO: PJZ: Better 
error message here?
+    }
+
+    return clusters;
+  }
+
+
+  private static Cluster discoverCluster(DiscoveryApiClient client, String 
clusterName) throws ApiException {
+    ClouderaManagerCluster cluster = null;
+
+    ServicesResourceApi servicesResourceApi = new ServicesResourceApi(client);
+    RolesResourceApi rolesResourceApi = new RolesResourceApi(client);
+
+    log.discoveringCluster(clusterName);
+
+    cluster = new ClouderaManagerCluster(clusterName);
+
+    Set<ServiceModel> serviceModels = new HashSet<>();
+    ServiceLoader<ServiceModelGenerator> loader = 
ServiceLoader.load(ServiceModelGenerator.class);
+
+    ApiServiceList serviceList = getClusterServices(servicesResourceApi, 
clusterName, client.isKerberos());
+    if (serviceList != null) {
+      for (ApiService service : serviceList.getItems()) {
+        String serviceName = service.getName();
+        log.discoveredService(serviceName, service.getType());
+        ApiServiceConfig serviceConfig =
+            getServiceConfig(servicesResourceApi, clusterName, serviceName, 
client.isKerberos());
+        ApiRoleList roleList = getRoles(rolesResourceApi, clusterName, 
serviceName, client.isKerberos());
+        if (roleList != null) {
+          for (ApiRole role : roleList.getItems()) {
+            String roleName = role.getName();
+            log.discoveredServiceRole(roleName, role.getType());
+            ApiConfigList roleConfig =
+                getRoleConfig(rolesResourceApi, clusterName, serviceName, 
roleName, client.isKerberos());
+
+            for (ServiceModelGenerator serviceModelGenerator : loader) {
+              if (serviceModelGenerator.handles(service, serviceConfig, role, 
roleConfig)) {
+                serviceModelGenerator.setApiClient(client);
+                ServiceModel serviceModel = 
serviceModelGenerator.generateService(service, serviceConfig, role, roleConfig);
+                serviceModels.add(serviceModel);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    cluster.addServiceModels(serviceModels);
+
+    return cluster;
+  }
+
+  private static ApiServiceList getClusterServices(final ServicesResourceApi 
servicesResourceApi,
+                                                   final String              
clusterName,
+                                                   final boolean             
isKerberos) {
+    ApiServiceList serviceList = null;
+    if (isKerberos) {
+      serviceList =
+          Subject.doAs(getSubject(), (PrivilegedAction<ApiServiceList>) () -> {
+            try {
+              return servicesResourceApi.readServices(clusterName, 
VIEW_SUMMARY);
+            } catch (Exception e) {
+              log.failedToAccessServiceConfigs(clusterName, e);
+            }
+            return null;
+          });
+    } else {
+      try {
+        serviceList = servicesResourceApi.readServices(clusterName, 
VIEW_SUMMARY);
+      } catch (ApiException e) {
+        log.failedToAccessServiceConfigs(clusterName, e);
+      }
+    }
+    return serviceList;
+  }
+
+  private static ApiServiceConfig getServiceConfig(final ServicesResourceApi 
servicesResourceApi,
+                                                   final String clusterName,
+                                                   final String serviceName,
+                                                   final boolean isKerberos) {
+    ApiServiceConfig serviceConfig = null;
+    if (isKerberos) {
+      serviceConfig =
+          Subject.doAs(getSubject(), (PrivilegedAction<ApiServiceConfig>) () 
-> {
+            try {
+              return servicesResourceApi.readServiceConfig(clusterName, 
serviceName, VIEW_FULL);
+            } catch (Exception e) {
+              log.failedToAccessServiceConfigs(clusterName, e);
+            }
+            return null;
+          });
+    } else {
+      try {
+        serviceConfig = servicesResourceApi.readServiceConfig(clusterName, 
serviceName, VIEW_FULL);
+      } catch (Exception e) {
+        log.failedToAccessServiceConfigs(clusterName, e);
+      }
+    }
+    return serviceConfig;
+  }
+
+  private static ApiRoleList getRoles(RolesResourceApi rolesResourceApi,
+                                      String clusterName,
+                                      String serviceName,
+                                      boolean isKerberos) {
+    ApiRoleList roleList = null;
+
+    if (isKerberos) {
+      roleList =
+          Subject.doAs(getSubject(), (PrivilegedAction<ApiRoleList>) () -> {
+            try {
+              return rolesResourceApi.readRoles(clusterName, serviceName, "", 
VIEW_SUMMARY);
+            } catch (Exception e) {
+              log.failedToAccessServiceRoleConfigs(clusterName, e);
+            }
+            return null;
+          });
+    } else {
+      try {
+        roleList = rolesResourceApi.readRoles(clusterName, serviceName, "", 
VIEW_SUMMARY);
+      } catch (ApiException e) {
+        log.failedToAccessServiceRoleConfigs(clusterName, e);
+      }
+    }
+
+    return roleList;
+  }
+
+  private static ApiConfigList getRoleConfig(RolesResourceApi rolesResourceApi,
+                                             String           clusterName,
+                                             String           serviceName,
+                                             String           roleName,
+                                             boolean          isKerberos) {
+    ApiConfigList roleConfig = null;
+    if (isKerberos) {
+      roleConfig =
+          Subject.doAs(getSubject(), (PrivilegedAction<ApiConfigList>) () -> {
+            try {
+              return rolesResourceApi.readRoleConfig(clusterName, roleName, 
serviceName, VIEW_FULL);
+            } catch (Exception e) {
+              log.failedToAccessServiceRoleConfigs(clusterName, e);
+            }
+            return null;
+          });
+    } else {
+      try {
+        roleConfig = rolesResourceApi.readRoleConfig(clusterName, roleName, 
serviceName, VIEW_FULL);
+      } catch (ApiException e) {
+        log.failedToAccessServiceRoleConfigs(clusterName, e);
+      }
+    }
+    return roleConfig;
+  }
+
+  private static Subject getSubject() {
+    Subject subject = SubjectUtils.getCurrentSubject();
+    if (subject == null) {
+      subject = login();
+    }
+    return subject;
+  }
+
+  private static Subject login() {
+    Subject subject = null;
+    String kerberosLoginConfig = getKerberosLoginConfig();
+    if (kerberosLoginConfig != null) {
+      try {
+        Configuration jaasConf = new JAASClientConfig((new 
File(kerberosLoginConfig)).toURI().toURL());
+        LoginContext lc = new LoginContext(JGSS_LOGIN_MODULE,
+                                           null,
+                                           null,
+                                           jaasConf);
+        lc.login();
+        subject = lc.getSubject();
+      } catch (Exception e) {
+        log.failedKerberosLogin(kerberosLoginConfig, JGSS_LOGIN_MODULE, e);
+      }
+    }
+
+    return subject;
+  }
+
+  private static final class JAASClientConfig extends Configuration {
+
+    private static final Configuration baseConfig = 
Configuration.getConfiguration();
+
+    private Configuration configFile;
+
+    JAASClientConfig(URL configFileURL) throws Exception {
+      if (configFileURL != null) {
+        this.configFile = ConfigurationFactory.create(configFileURL.toURI());
+      }
+    }
+
+    @Override
+    public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+      AppConfigurationEntry[] result = null;
+
+      // Try the config file if it exists
+      if (configFile != null) {
+        result = configFile.getAppConfigurationEntry(name);
+      }
+
+      // If the entry isn't there, delegate to the base configuration
+      if (result == null) {
+        result = baseConfig.getAppConfigurationEntry(name);
+      }
+
+      return result;
+    }
+  }
+
+  @SuppressWarnings("PMD.AvoidAccessibilityAlteration")
+  private static class ConfigurationFactory {
+
+    private static final Class implClazz;
+    static {
+      // Oracle and OpenJDK use the Sun implementation
+      String implName = System.getProperty("java.vendor").contains("IBM") ?
+          "com.ibm.security.auth.login.ConfigFile" : 
"com.sun.security.auth.login.ConfigFile";
+
+      log.usingJAASConfigurationFileImplementation(implName);
+      Class clazz = null;
+      try {
+        clazz = Class.forName(implName, false, 
Thread.currentThread().getContextClassLoader());
+      } catch (ClassNotFoundException e) {
+        log.failedToLoadJAASConfigurationFileImplementation(implName, e);
+      }
+
+      implClazz = clazz;
+    }
+
+    static Configuration create(URI uri) {
+      Configuration config = null;
+
+      if (implClazz != null) {
+        try {
+          Constructor ctor = implClazz.getDeclaredConstructor(URI.class);
+          config = (Configuration) ctor.newInstance(uri);
+        } catch (Exception e) {
+          
log.failedToInstantiateJAASConfigurationFileImplementation(implClazz.getCanonicalName(),
 e);
+        }
+      } else {
+        log.noJAASConfigurationFileImplementation();
+      }
+
+      return config;
+    }
+  }
+
+  private static String getKerberosLoginConfig() {
+    return System.getProperty(GatewayConfig.KRB5_LOGIN_CONFIG, "");
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryMessages.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryMessages.java
new file mode 100644
index 0000000..21e2049
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryMessages.java
@@ -0,0 +1,97 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import org.apache.knox.gateway.i18n.messages.Message;
+import org.apache.knox.gateway.i18n.messages.MessageLevel;
+import org.apache.knox.gateway.i18n.messages.Messages;
+import org.apache.knox.gateway.i18n.messages.StackTrace;
+
+@Messages(logger="org.apache.knox.gateway.topology.discovery.cm")
+public interface ClouderaManagerServiceDiscoveryMessages {
+
+  @Message(level = MessageLevel.INFO, text = "Discovered cluster: {0} ({1})")
+  void discoveredCluster(String clusterName, String version);
+
+  @Message(level = MessageLevel.INFO, text = "Performing cluster discovery for 
\"{0}\"")
+  void discoveringCluster(String clusterName);
+
+  @Message(level = MessageLevel.INFO, text = "Discovered service: {0} ({1})")
+  void discoveredService(String serviceName, String serviceType);
+
+  @Message(level = MessageLevel.INFO, text = "Discovered service role: {0} 
({1})")
+  void discoveredServiceRole(String roleName, String roleType);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Failed Kerberos login {0} ({1}): {2}")
+  void failedKerberosLogin(String jaasLoginConfig,
+                           String entryName,
+                           @StackTrace(level = MessageLevel.DEBUG) Exception 
e);
+
+  @Message(level = MessageLevel.DEBUG, text = "Using JAAS configuration file 
implementation: {0}")
+  void usingJAASConfigurationFileImplementation(String implementation);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Failed to load JAAS configuration file implementation {0}: {1}")
+  void failedToLoadJAASConfigurationFileImplementation(String 
implementationName,
+                                                       @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Failed to instantiate JAAS configuration file implementation 
{0}: {1}")
+  void failedToInstantiateJAASConfigurationFileImplementation(String 
implementationName,
+                                                       @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR, text = "No JAAS configuration file 
implementation found.")
+  void noJAASConfigurationFileImplementation();
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Encountered an error during cluster ({0}) discovery: {1}")
+  void clusterDiscoveryError(String clusterName, @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Failed to access the service configurations for cluster ({0}) 
discovery")
+  void failedToAccessServiceConfigs(String clusterName, @StackTrace(level = 
MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Failed to access the service role configurations for cluster 
({0}) discovery")
+  void failedToAccessServiceRoleConfigs(String clusterName, @StackTrace(level 
= MessageLevel.DEBUG) Exception e);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "No address for Cloudera Manager service discovery has been 
configured.")
+  void missingDiscoveryAddress();
+
+  @Message(level = MessageLevel.ERROR,
+      text = "No cluster for Cloudera Manager service discovery has been 
configured.")
+  void missingDiscoveryCluster();
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Encountered an error attempting to determine the user for alias 
{0} : {1}")
+  void aliasServiceUserError(String alias, String error);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "Encountered an error attempting to determine the password for 
alias {0} : {1}")
+  void aliasServicePasswordError(String alias, String error);
+
+  @Message(level = MessageLevel.ERROR,
+      text = "No user configured for Cloudera Manager service discovery.")
+  void aliasServiceUserNotFound();
+
+  @Message(level = MessageLevel.ERROR,
+      text = "No password configured for Cloudera Manager service discovery.")
+  void aliasServicePasswordNotFound();
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryType.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryType.java
new file mode 100644
index 0000000..b5949da
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryType.java
@@ -0,0 +1,37 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType;
+
+/**
+ * ClouderaManager-based service discovery implementation type.
+ */
+public class ClouderaManagerServiceDiscoveryType implements 
ServiceDiscoveryType {
+
+  @Override
+  public String getType() {
+    return ClouderaManagerServiceDiscovery.TYPE;
+  }
+
+  @Override
+  public ServiceDiscovery newInstance() {
+    return new ClouderaManagerServiceDiscovery();
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java
new file mode 100644
index 0000000..901586c
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java
@@ -0,0 +1,104 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import com.cloudera.api.swagger.client.ApiClient;
+import org.apache.knox.gateway.config.ConfigurationException;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.AliasServiceException;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
+
+import static 
org.apache.knox.gateway.topology.discovery.cm.ClouderaManagerServiceDiscovery.API_PATH;
+import static 
org.apache.knox.gateway.topology.discovery.cm.ClouderaManagerServiceDiscovery.DEFAULT_USER_ALIAS;
+import static 
org.apache.knox.gateway.topology.discovery.cm.ClouderaManagerServiceDiscovery.DEFAULT_PWD_ALIAS;
+
+
+public class DiscoveryApiClient extends ApiClient {
+
+  private ClouderaManagerServiceDiscoveryMessages log =
+      MessagesFactory.get(ClouderaManagerServiceDiscoveryMessages.class);
+
+  private boolean isKerberos;
+
+  DiscoveryApiClient(ServiceDiscoveryConfig discoveryConfig, AliasService 
aliasService) {
+    configure(discoveryConfig, aliasService);
+  }
+
+  boolean isKerberos() {
+    return isKerberos;
+  }
+
+  private void configure(ServiceDiscoveryConfig config, AliasService 
aliasService) {
+    String apiAddress = config.getAddress();
+    apiAddress += (apiAddress.endsWith("/") ? API_PATH : "/" + API_PATH);
+
+    setBasePath(apiAddress);
+
+    String username = config.getUser();
+    String passwordAlias = config.getPasswordAlias();
+
+    String password = null;
+    // If no configured username, then use default username alias
+    if (username == null) {
+      if (aliasService != null) {
+        try {
+          char[] defaultUser = 
aliasService.getPasswordFromAliasForGateway(DEFAULT_USER_ALIAS);
+          if (defaultUser != null) {
+            username = new String(defaultUser);
+          }
+        } catch (AliasServiceException e) {
+          log.aliasServiceUserError(DEFAULT_USER_ALIAS, 
e.getLocalizedMessage());
+        }
+      }
+
+      // If username is still null
+      if (username == null) {
+        log.aliasServiceUserNotFound();
+        throw new ConfigurationException("No username is configured for 
Cloudera Manager service discovery.");
+      }
+    }
+
+    if (aliasService != null) {
+      // If no password alias is configured, then try the default alias
+      if (passwordAlias == null) {
+        passwordAlias = DEFAULT_PWD_ALIAS;
+      }
+
+      try {
+        char[] pwd = 
aliasService.getPasswordFromAliasForGateway(passwordAlias);
+        if (pwd != null) {
+          password = new String(pwd);
+        }
+
+      } catch (AliasServiceException e) {
+        log.aliasServicePasswordError(passwordAlias, e.getLocalizedMessage());
+      }
+    }
+
+    // If the password could not be determined
+    if (password == null) {
+      log.aliasServicePasswordNotFound();
+      isKerberos = Boolean.getBoolean(GatewayConfig.HADOOP_KERBEROS_SECURED);
+    }
+
+    setUsername(username);
+    setPassword(password);
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
new file mode 100644
index 0000000..6e4f09e
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
@@ -0,0 +1,73 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import java.util.Objects;
+
+public class ServiceModel {
+
+  public enum Type {API, UI}
+
+  private final Type type;
+  private final String service;
+  private final String serviceUrl;
+
+  public ServiceModel(Type   type,
+                      String service,
+                      String serviceUrl) {
+    this.type = type;
+    this.service = service;
+    this.serviceUrl = serviceUrl;
+  }
+
+  public Type getType() {
+    return type;
+  }
+
+  public String getService() {
+    return service;
+  }
+
+  public String getServiceUrl() {
+    return serviceUrl;
+  }
+
+  @Override
+  public String toString() {
+    return getService() + '-' + getServiceUrl();
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(type, service, serviceUrl);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    ServiceModel other = (ServiceModel) obj;
+    return getType().equals(other.getType()) &&
+           getService().equals(other.getService()) &&
+           getServiceUrl().equals(other.getServiceUrl());
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModelGenerator.java
new file mode 100644
index 0000000..6c0cdc8
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModelGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+
+public interface ServiceModelGenerator {
+
+  void setApiClient(DiscoveryApiClient client);
+
+  boolean handles(ApiService       service,
+                  ApiServiceConfig serviceConfig,
+                  ApiRole          role,
+                  ApiConfigList    roleConfig);
+
+  ServiceModel generateService(ApiService       service,
+                               ApiServiceConfig serviceConfig,
+                               ApiRole          role,
+                               ApiConfigList    roleConfig) throws 
ApiException;
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
new file mode 100644
index 0000000..e4badc9
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
@@ -0,0 +1,76 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model;
+
+import com.cloudera.api.swagger.client.ApiClient;
+import com.cloudera.api.swagger.model.ApiConfig;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.DiscoveryApiClient;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModelGenerator;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class AbstractServiceModelGenerator implements 
ServiceModelGenerator {
+
+  private DiscoveryApiClient client;
+
+  @Override
+  public void setApiClient(DiscoveryApiClient client) {
+    this.client = client;
+  }
+
+  protected ApiClient getClient() {
+    return client;
+  }
+
+  protected String getServiceConfigValue(ApiServiceConfig serviceConfig, 
String key) {
+    return getConfigValue(key, serviceConfig.getItems());
+  }
+
+  protected String getRoleConfigValue(ApiConfigList roleConfig, String key) {
+    return getConfigValue(key, roleConfig.getItems());
+  }
+
+  protected String getConfigValue(String key, List<ApiConfig> items) {
+    for (ApiConfig config : items) {
+      if (key.equals(config.getName())) {
+        String value = config.getValue();
+        if (value != null) {
+          return value;
+        }
+        return config.getDefault();
+      }
+    }
+    return null;
+  }
+
+  protected String getSafetyValveValue(String safetyValve, String key) {
+    String val = null;
+    if (safetyValve != null) {
+      Pattern p = Pattern.compile("<property><name>" + key + 
"</name><value>(.*?)</value></property>");
+      Matcher m = p.matcher(safetyValve.replaceAll("\\s",""));
+      if (m.find()) {
+        val = m.group(1);
+      }
+    }
+    return val;
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasAPIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasAPIServiceModelGenerator.java
new file mode 100644
index 0000000..32edd66
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasAPIServiceModelGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.atlas;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class AtlasAPIServiceModelGenerator extends AtlasServiceModelGenerator {
+  private static final String SERVICE = "ATLAS-API";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    return new ServiceModel(ServiceModel.Type.API,
+                           SERVICE,
+                           super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl());
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
new file mode 100644
index 0000000..3888d2e
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.atlas;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class AtlasServiceModelGenerator extends AbstractServiceModelGenerator {
+  private static final String SERVICE = "ATLAS";
+  private static final String SERVICE_TYPE = "ATLAS";
+  private static final String ROLE_TYPE = "ATLAS_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, "atlas_server_https_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "atlas_server_http_port");
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasUIServiceModelGenerator.java
new file mode 100644
index 0000000..43f389a
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasUIServiceModelGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.atlas;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class AtlasUIServiceModelGenerator extends 
AtlasAPIServiceModelGenerator {
+  private static final String SERVICE = "ATLAS-API"; // TODO: PJZ: Should this 
really be -API?
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            super.generateService(service, serviceConfig, 
role, roleConfig).getServiceUrl());
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/HBaseUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/HBaseUIServiceModelGenerator.java
new file mode 100644
index 0000000..59fbf7d
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/HBaseUIServiceModelGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hbase;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class HBaseUIServiceModelGenerator extends 
AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "HBASEUI";
+  private static final String SERVICE_TYPE = "HBASE";
+  private static final String ROLE_TYPE = "MASTER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port = getRoleConfigValue(roleConfig, "hbase_master_info_port");
+    boolean sslEnabled = 
Boolean.parseBoolean(getServiceConfigValue(serviceConfig, 
"hbase_hadoop_ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+    } else {
+      scheme = "http";
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/WebHBaseServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/WebHBaseServiceModelGenerator.java
new file mode 100644
index 0000000..c47babd
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hbase/WebHBaseServiceModelGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hbase;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class WebHBaseServiceModelGenerator extends 
AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "WEBHBASE";
+  private static final String SERVICE_TYPE = "HBASE";
+  private static final String ROLE_TYPE = "HBASERESTSERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port = getRoleConfigValue(roleConfig, "hbase_restserver_port");
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"hbase_restserver_ssl_enable"));
+    if(sslEnabled) {
+      scheme = "https";
+    } else {
+      scheme = "http";
+    }
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/HdfsUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/HdfsUIServiceModelGenerator.java
new file mode 100644
index 0000000..522f958
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/HdfsUIServiceModelGenerator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hdfs;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+import java.util.Locale;
+
+public class HdfsUIServiceModelGenerator extends NameNodeServiceModelGenerator 
{
+  private static final String SERVICE = "HDFSUI";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = 
Boolean.parseBoolean(getServiceConfigValue(serviceConfig, 
"hdfs_hadoop_ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, "dfs_https_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "dfs_http_port");
+    }
+    String namenodeUrl = String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port);
+    return new ServiceModel(ServiceModel.Type.UI, SERVICE, namenodeUrl);
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/NameNodeServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/NameNodeServiceModelGenerator.java
new file mode 100644
index 0000000..230a1f3
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/NameNodeServiceModelGenerator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hdfs;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class NameNodeServiceModelGenerator extends 
AbstractServiceModelGenerator {
+  private static final String SERVICE      = "NAMENODE";
+  private static final String SERVICE_TYPE = "HDFS";
+  private static final String ROLE_TYPE    = "NAMENODE";
+
+  @Override
+  public boolean handles(ApiService       service,
+                         ApiServiceConfig serviceConfig,
+                         ApiRole          role,
+                         ApiConfigList    roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    boolean haEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"autofailover_enabled"));
+    String serviceUrl;
+    if(haEnabled) {
+      String nameservice = getRoleConfigValue(roleConfig, 
"dfs_federation_namenode_nameservice");
+      serviceUrl = String.format(Locale.getDefault(), "hdfs://%s", 
nameservice);
+    } else {
+      String hostname = role.getHostRef().getHostname();
+      String port = getRoleConfigValue(roleConfig, "namenode_port");
+      serviceUrl = String.format(Locale.getDefault(), "hdfs://%s:%s", 
hostname, port);
+    }
+    return new ServiceModel(ServiceModel.Type.API, SERVICE, serviceUrl);
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/WebHdfsServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/WebHdfsServiceModelGenerator.java
new file mode 100644
index 0000000..469ac39
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hdfs/WebHdfsServiceModelGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hdfs;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class WebHdfsServiceModelGenerator extends HdfsUIServiceModelGenerator {
+  private static final String SERVICE = "WEBHDFS";
+  private static final String WEBHDFS_SUFFIX = "/webhdfs";
+
+  @Override
+  public boolean handles(ApiService       service,
+                         ApiServiceConfig serviceConfig,
+                         ApiRole          role,
+                         ApiConfigList    roleConfig) {
+    return super.handles(service, serviceConfig, role, roleConfig) &&
+           Boolean.parseBoolean(getServiceConfigValue(serviceConfig, 
"dfs_webhdfs_enabled"));
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String serviceUrl =
+        super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl() + WEBHDFS_SUFFIX;
+    return new ServiceModel(ServiceModel.Type.API, SERVICE, serviceUrl);
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hive/HiveServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hive/HiveServiceModelGenerator.java
new file mode 100644
index 0000000..f01ff02
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hive/HiveServiceModelGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hive;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class HiveServiceModelGenerator extends AbstractServiceModelGenerator {
+
+  private static final String SERVICE      = "HIVE";
+  private static final String SERVICE_TYPE = "HIVE";
+  private static final String ROLE_TYPE    = "HIVESERVER2";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType()) && checkHiveServer2HTTPMode(roleConfig);
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String hostname = role.getHostRef().getHostname();
+    String hs2SafetyValve = getRoleConfigValue(roleConfig, 
"hive_hs2_config_safety_valve");
+    String port = getSafetyValveValue(hs2SafetyValve, 
"hive.server2.thrift.http.port");
+    String httpPath = getSafetyValveValue(hs2SafetyValve, 
"hive.server2.thrift.http.path");
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"hive.server2.use.SSL"));
+    String scheme = sslEnabled ? "https" : "http";
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), 
"%s://%s:%s/%s", scheme, hostname, port, httpPath));
+  }
+
+  private boolean checkHiveServer2HTTPMode(ApiConfigList roleConfig) {
+    String hiveServer2SafetyValve = getRoleConfigValue(roleConfig, 
"hive_hs2_config_safety_valve");
+    if(hiveServer2SafetyValve != null) {
+      String transportMode = getSafetyValveValue(hiveServer2SafetyValve, 
"hive.server2.transport.mode");
+      return "http".equals(transportMode);
+    }
+    return false;
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
new file mode 100644
index 0000000..63eb0b6
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hue;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class HueLBServiceModelGenerator extends AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "HUE";
+  private static final String SERVICE_TYPE = "HUE";
+  private static final String ROLE_TYPE = "HUE_LOAD_BALANCER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme = "http";
+    String port = getRoleConfigValue(roleConfig, "listen");
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
new file mode 100644
index 0000000..12e4eb3
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.hue;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class HueServiceModelGenerator extends AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "HUE";
+  private static final String SERVICE_TYPE = "HUE";
+  private static final String ROLE_TYPE = "HUE_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port = getRoleConfigValue(roleConfig, "hue_http_port");
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enable"));
+    if(sslEnabled) {
+      scheme = "https";
+    } else {
+      scheme = "http";
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyServiceModelGenerator.java
new file mode 100644
index 0000000..48c3f9d
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyServiceModelGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.livy;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class LivyServiceModelGenerator extends AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "LIVYSERVER";
+  private static final String SERVICE_TYPE = "LIVY";
+  private static final String ROLE_TYPE = "LIVY_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port = getRoleConfigValue(roleConfig, "livy_server_port");
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+    } else {
+      scheme = "http";
+    }
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyUIServiceModelGenerator.java
new file mode 100644
index 0000000..3c8160a
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/livy/LivyUIServiceModelGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.livy;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class LivyUIServiceModelGenerator extends LivyServiceModelGenerator {
+
+  private static final String SERVICE = "LIVYSERVER";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String serviceUrl = super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl();
+    return new ServiceModel(ServiceModel.Type.UI, SERVICE, serviceUrl);
+  }
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieServiceModelGenerator.java
new file mode 100644
index 0000000..d31c020
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieServiceModelGenerator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.oozie;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class OozieServiceModelGenerator extends AbstractServiceModelGenerator {
+
+  private static final String SERVICE = "OOZIE";
+  private static final String SERVICE_TYPE = "OOZIE";
+  private static final String ROLE_TYPE = "OOZIE_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = 
Boolean.parseBoolean(getServiceConfigValue(serviceConfig, "oozie_use_ssl"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, "oozie_https_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "oozie_http_port");
+    }
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), 
"%s://%s:%s/oozie/", scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieUIServiceModelGenerator.java
new file mode 100644
index 0000000..d5d8b27
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/oozie/OozieUIServiceModelGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.oozie;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class OozieUIServiceModelGenerator extends OozieServiceModelGenerator {
+  private static final String SERVICE = "OOZIEUI";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            super.generateService(service, serviceConfig, 
role, roleConfig).getServiceUrl());
+  }
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerServiceModelGenerator.java
new file mode 100644
index 0000000..f39f707
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerServiceModelGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.ranger;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class RangerServiceModelGenerator extends AbstractServiceModelGenerator 
{
+  private static final String SERVICE = "RANGER";
+  private static final String SERVICE_TYPE = "RANGER";
+  private static final String ROLE_TYPE = "RANGER_ADMIN";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getServiceConfigValue(serviceConfig, "ranger_service_https_port");
+    } else {
+      scheme = "http";
+      port = getServiceConfigValue(serviceConfig, "ranger_service_http_port");
+    }
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIAPIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIAPIServiceModelGenerator.java
new file mode 100644
index 0000000..b635547
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIAPIServiceModelGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.ranger;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class RangerUIAPIServiceModelGenerator extends 
RangerServiceModelGenerator {
+  private static final String SERVICE = "RANGER";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            super.generateService(service, serviceConfig, 
role, roleConfig).getServiceUrl());
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIServiceModelGenerator.java
new file mode 100644
index 0000000..5ffd097
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/ranger/RangerUIServiceModelGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.ranger;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class RangerUIServiceModelGenerator extends RangerServiceModelGenerator 
{
+  private static final String SERVICE = "RANGERUI";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String serviceUrl = super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl();
+    return new ServiceModel(ServiceModel.Type.UI, SERVICE, serviceUrl);
+  }
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrServiceModelGenerator.java
new file mode 100644
index 0000000..bc6472b
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrServiceModelGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.solr;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class SolrServiceModelGenerator extends AbstractServiceModelGenerator {
+  private static final String SERVICE = "SOLR";
+  private static final String SERVICE_TYPE = "SOLR";
+  private static final String ROLE_TYPE = "SOLR_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = 
Boolean.parseBoolean(getServiceConfigValue(serviceConfig, "solr_use_ssl"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, "solr_https_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "solr_http_port");
+    }
+    return new ServiceModel(ServiceModel.Type.API,
+                            SERVICE,
+                            String.format(Locale.getDefault(), 
"%s://%s:%s/solr/", scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrUIServiceModelGenerator.java
new file mode 100644
index 0000000..301ba0f
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/solr/SolrUIServiceModelGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.solr;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class SolrUIServiceModelGenerator extends SolrServiceModelGenerator {
+  private static final String SERVICE = "SOLR";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String serviceUrl = super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl();
+    return new ServiceModel(ServiceModel.Type.UI, SERVICE, serviceUrl);
+  }
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/spark/SparkHistoryUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/spark/SparkHistoryUIServiceModelGenerator.java
new file mode 100644
index 0000000..62b122c
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/spark/SparkHistoryUIServiceModelGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.spark;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class SparkHistoryUIServiceModelGenerator extends 
AbstractServiceModelGenerator {
+  private static final String SERVICE = "SPARKHISTORYUI";
+  private static final String SERVICE_TYPE = "SPARK_ON_YARN";
+  private static final String ROLE_TYPE = "SPARK_YARN_HISTORY_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+    if(sslEnabled) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, "ssl_server_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "history_server_web_port");
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/JobHistoryUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/JobHistoryUIServiceModelGenerator.java
new file mode 100644
index 0000000..b9f5609
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/JobHistoryUIServiceModelGenerator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.yarn;
+
+import com.cloudera.api.swagger.ServicesResourceApi;
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class JobHistoryUIServiceModelGenerator extends 
AbstractServiceModelGenerator {
+  private static final String SERVICE = "JOBHISTORYUI";
+  private static final String SERVICE_TYPE = "YARN";
+  private static final String ROLE_TYPE = "JOBHISTORY";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+
+    if(isSSLEnabled(service, serviceConfig)) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, 
"mapreduce_jobhistory_webapp_https_address");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, 
"mapreduce_jobhistory_webapp_address");
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+  private boolean isSSLEnabled(ApiService service, ApiServiceConfig 
serviceConfig)
+      throws ApiException {
+    ServicesResourceApi servicesResourceApi = new 
ServicesResourceApi(getClient());
+    String clusterName = service.getClusterRef().getClusterName();
+    String hdfsService = getServiceConfigValue(serviceConfig, "hdfs_service");
+    ApiServiceConfig hdfsServiceConfig = 
servicesResourceApi.readServiceConfig(clusterName, hdfsService, "full");
+    return Boolean.parseBoolean(getServiceConfigValue(hdfsServiceConfig, 
"hdfs_hadoop_ssl_enabled"));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/ResourceManagerUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/ResourceManagerUIServiceModelGenerator.java
new file mode 100644
index 0000000..6954045
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/ResourceManagerUIServiceModelGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.yarn;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class ResourceManagerUIServiceModelGenerator extends 
YarnUIServiceModelGenerator {
+  private static final String SERVICE = "RESOURCEMANAGER";
+  private static final String RM_WS_SUFFIX = "/ws";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String url =  super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl() + RM_WS_SUFFIX;
+    return new ServiceModel(ServiceModel.Type.API, SERVICE, url);
+  }
+
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIServiceModelGenerator.java
new file mode 100644
index 0000000..25136cb
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIServiceModelGenerator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.yarn;
+
+import com.cloudera.api.swagger.ServicesResourceApi;
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class YarnUIServiceModelGenerator extends AbstractServiceModelGenerator 
{
+  private static final String SERVICE = "YARNUI";
+  private static final String SERVICE_TYPE = "YARN";
+  private static final String ROLE_TYPE = "RESOURCEMANAGER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String hostname = role.getHostRef().getHostname();
+    String scheme;
+    String port;
+
+    if(isSSLEnabled(service, serviceConfig)) {
+      scheme = "https";
+      port = getRoleConfigValue(roleConfig, 
"resourcemanager_webserver_https_port");
+    } else {
+      scheme = "http";
+      port = getRoleConfigValue(roleConfig, "resourcemanager_webserver_port");
+    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+  private boolean isSSLEnabled(ApiService service, ApiServiceConfig 
serviceConfig)
+      throws ApiException {
+    ServicesResourceApi servicesResourceApi = new 
ServicesResourceApi(getClient());
+    String clusterName = service.getClusterRef().getClusterName();
+    String hdfsService = getServiceConfigValue(serviceConfig, "hdfs_service");
+    ApiServiceConfig hdfsServiceConfig = 
servicesResourceApi.readServiceConfig(clusterName, hdfsService, "full");
+    return Boolean.parseBoolean(getServiceConfigValue(hdfsServiceConfig, 
"hdfs_hadoop_ssl_enabled"));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIv2ServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIv2ServiceModelGenerator.java
new file mode 100644
index 0000000..b71a7fe
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/yarn/YarnUIv2ServiceModelGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.yarn;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+public class YarnUIv2ServiceModelGenerator extends YarnUIServiceModelGenerator 
{
+  private static final String SERVICE = "YARNUIV2";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) throws 
ApiException {
+    String serviceUrl = super.generateService(service, serviceConfig, role, 
roleConfig).getServiceUrl();
+    return new ServiceModel(ServiceModel.Type.UI, SERVICE, serviceUrl);
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinUIServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinUIServiceModelGenerator.java
new file mode 100644
index 0000000..72b4e8c
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinUIServiceModelGenerator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.zeppelin;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import 
org.apache.knox.gateway.topology.discovery.cm.model.AbstractServiceModelGenerator;
+
+import java.util.Locale;
+
+public class ZeppelinUIServiceModelGenerator extends 
AbstractServiceModelGenerator {
+  private static final String SERVICE = "ZEPPELINUI";
+  private static final String SERVICE_TYPE = "ZEPPELIN";
+  private static final String ROLE_TYPE = "ZEPPELIN_SERVER";
+
+  @Override
+  public boolean handles(ApiService service, ApiServiceConfig serviceConfig, 
ApiRole role, ApiConfigList roleConfig) {
+    return SERVICE_TYPE.equals(service.getType()) && 
ROLE_TYPE.equals(role.getType());
+  }
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme = "http";
+    String port = getRoleConfigValue(roleConfig, "zeppelin_server_port");
+//    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+//    if(sslEnabled) {
+//      scheme = "https";
+//    } else {
+//      scheme = "http";
+//    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinWSServiceModelGenerator.java
 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinWSServiceModelGenerator.java
new file mode 100644
index 0000000..bdf91b5
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/zeppelin/ZeppelinWSServiceModelGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm.model.zeppelin;
+
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+
+import java.util.Locale;
+
+public class ZeppelinWSServiceModelGenerator extends 
ZeppelinUIServiceModelGenerator {
+  private static final String SERVICE = "ZEPPELINWS";
+
+  @Override
+  public ServiceModel generateService(ApiService       service,
+                                      ApiServiceConfig serviceConfig,
+                                      ApiRole          role,
+                                      ApiConfigList    roleConfig) {
+    String hostname = role.getHostRef().getHostname();
+    String scheme = "ws";
+    String port = getRoleConfigValue(roleConfig, "zeppelin_server_port");
+//    boolean sslEnabled = Boolean.parseBoolean(getRoleConfigValue(roleConfig, 
"ssl_enabled"));
+//    if(sslEnabled) {
+//      scheme = "wss";
+//    } else {
+//      scheme = "ws";
+//    }
+    return new ServiceModel(ServiceModel.Type.UI,
+                            SERVICE,
+                            String.format(Locale.getDefault(), "%s://%s:%s", 
scheme, hostname, port));
+  }
+
+}
diff --git 
a/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
 
b/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
new file mode 100644
index 0000000..e199dfb
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.ServiceDiscoveryType
@@ -0,0 +1,19 @@
+##########################################################################
+# 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.
+##########################################################################
+
+org.apache.knox.gateway.topology.discovery.cm.ClouderaManagerServiceDiscoveryType
diff --git 
a/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.cm.ServiceModelGenerator
 
b/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.cm.ServiceModelGenerator
new file mode 100644
index 0000000..b4f485c
--- /dev/null
+++ 
b/gateway-discovery-cm/src/main/resources/META-INF/services/org.apache.knox.gateway.topology.discovery.cm.ServiceModelGenerator
@@ -0,0 +1,44 @@
+##########################################################################
+# 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.
+##########################################################################
+
+org.apache.knox.gateway.topology.discovery.cm.model.atlas.AtlasServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.atlas.AtlasAPIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.atlas.AtlasUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hbase.HBaseUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hbase.WebHBaseServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hdfs.NameNodeServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hdfs.HdfsUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hdfs.WebHdfsServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hive.HiveServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hue.HueServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.hue.HueLBServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.livy.LivyServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.livy.LivyUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.oozie.OozieServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.oozie.OozieUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.ranger.RangerServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.ranger.RangerUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.ranger.RangerUIAPIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.solr.SolrServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.solr.SolrUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.spark.SparkHistoryUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.yarn.JobHistoryUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.yarn.ResourceManagerUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.yarn.YarnUIServiceModelGenerator
+org.apache.knox.gateway.topology.discovery.cm.model.yarn.YarnUIv2ServiceModelGenerator
+
diff --git 
a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
 
b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
new file mode 100644
index 0000000..7c32c43
--- /dev/null
+++ 
b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java
@@ -0,0 +1,283 @@
+/*
+ * 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.knox.gateway.topology.discovery.cm;
+
+import com.cloudera.api.swagger.client.ApiException;
+import com.cloudera.api.swagger.client.ApiResponse;
+import com.cloudera.api.swagger.model.ApiConfig;
+import com.cloudera.api.swagger.model.ApiConfigList;
+import com.cloudera.api.swagger.model.ApiHostRef;
+import com.cloudera.api.swagger.model.ApiRole;
+import com.cloudera.api.swagger.model.ApiRoleList;
+import com.cloudera.api.swagger.model.ApiService;
+import com.cloudera.api.swagger.model.ApiServiceConfig;
+import com.cloudera.api.swagger.model.ApiServiceList;
+import com.squareup.okhttp.Call;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+
+public class ClouderaManagerServiceDiscoveryTest {
+
+  @Test
+  public void testWebHDFSServiceDiscovery() {
+    GatewayConfig gwConf = EasyMock.createNiceMock(GatewayConfig.class);
+    EasyMock.replay(gwConf);
+
+    ServiceDiscoveryConfig sdConfig = createMockDiscoveryConfig();
+
+    // Create the test client for providing test response content
+    TestDiscoveryApiClient mockClient = new TestDiscoveryApiClient(sdConfig, 
null);
+
+    // Prepare the service list response for the cluster
+    ApiServiceList serviceList = EasyMock.createNiceMock(ApiServiceList.class);
+    EasyMock.expect(serviceList.getItems())
+            
.andReturn(Collections.singletonList(createMockApiService("NAMENODE-1", 
"HDFS")))
+            .anyTimes();
+    EasyMock.replay(serviceList);
+    mockClient.addResponse(ApiServiceList.class, new 
TestApiServiceListResponse(serviceList));
+
+    // Prepare the HDFS service config response for the cluster
+    Map<String, String> serviceProps = new HashMap<>();
+    serviceProps.put("hdfs_hadoop_ssl_enabled", "false");
+    serviceProps.put("dfs_webhdfs_enabled", "true");
+    ApiServiceConfig hdfsServiceConfig = 
createMockApiServiceConfig(serviceProps);
+    mockClient.addResponse(ApiServiceConfig.class, new 
TestApiServiceConfigResponse(hdfsServiceConfig));
+
+    // Prepare the NameNode role
+    ApiRole nnRole = 
createMockApiRole("HDFS-1-NAMENODE-d0b64dd7b7611e22bc976ede61678d9e", 
"NAMENODE", "test-host-1");
+    ApiRoleList nnRoleList = EasyMock.createNiceMock(ApiRoleList.class);
+    
EasyMock.expect(nnRoleList.getItems()).andReturn(Collections.singletonList(nnRole)).anyTimes();
+    EasyMock.replay(nnRoleList);
+    mockClient.addResponse(ApiRoleList.class, new 
TestApiRoleListResponse(nnRoleList));
+
+    // Configure the NameNode role
+    Map<String, String> roleProperties = new HashMap<>();
+    roleProperties.put("dfs_federation_namenode_nameservice", "nameservice1");
+    roleProperties.put("namenode_port", "50070");
+    roleProperties.put("dfs_http_port", "50071");
+    ApiConfigList nnRoleConfigList = createMockApiConfigList(roleProperties);
+    mockClient.addResponse(ApiConfigList.class, new 
TestApiConfigListResponse(nnRoleConfigList));
+
+    // Invoke the service discovery
+    ClouderaManagerServiceDiscovery cmsd = new 
ClouderaManagerServiceDiscovery(true);
+    ServiceDiscovery.Cluster cluster = cmsd.discover(gwConf, sdConfig, 
"test-cluster", mockClient);
+    assertNotNull(cluster);
+    assertEquals("test-cluster", cluster.getName());
+    List<String> webhdfsURLs = cluster.getServiceURLs("WEBHDFS");
+    assertNotNull(webhdfsURLs);
+    assertEquals(1, webhdfsURLs.size());
+    assertEquals("http://test-host-1:50071/webhdfs";, webhdfsURLs.get(0));
+  }
+
+  @Test
+  public void testHiveServiceDiscovery() {
+    GatewayConfig gwConf = EasyMock.createNiceMock(GatewayConfig.class);
+    EasyMock.replay(gwConf);
+
+    ServiceDiscoveryConfig sdConfig = createMockDiscoveryConfig();
+
+    // Create the test client for providing test response content
+    TestDiscoveryApiClient mockClient = new TestDiscoveryApiClient(sdConfig, 
null);
+
+    // Prepare the service list response for the cluster
+    ApiServiceList serviceList = EasyMock.createNiceMock(ApiServiceList.class);
+    EasyMock.expect(serviceList.getItems())
+            
.andReturn(Collections.singletonList(createMockApiService("HIVE-1", "HIVE")))
+            .anyTimes();
+    EasyMock.replay(serviceList);
+    mockClient.addResponse(ApiServiceList.class, new 
TestApiServiceListResponse(serviceList));
+
+    // Prepare the HIVE service config response for the cluster
+    ApiServiceConfig hiveServiceConfig = createMockApiServiceConfig();
+    mockClient.addResponse(ApiServiceConfig.class, new 
TestApiServiceConfigResponse(hiveServiceConfig));
+
+    // Prepare the HS2 role
+    ApiRole hs2Role = 
createMockApiRole("HIVE-1-HIVESERVER2-d0b64dd7b7611e22bc976ede61678d9e", 
"HIVESERVER2", "test-host-1");
+    ApiRoleList hiveRoleList = EasyMock.createNiceMock(ApiRoleList.class);
+    
EasyMock.expect(hiveRoleList.getItems()).andReturn(Collections.singletonList(hs2Role)).anyTimes();
+    EasyMock.replay(hiveRoleList);
+    mockClient.addResponse(ApiRoleList.class, new 
TestApiRoleListResponse(hiveRoleList));
+
+    // Configure the HS2 role
+    Map<String, String> roleProperties = new HashMap<>();
+    roleProperties.put("hive_hs2_config_safety_valve",
+                       
"<property><name>hive.server2.transport.mode</name><value>http</value></property>\n"
 +
+                       
"<property><name>hive.server2.thrift.http.port</name><value>10001</value></property>\n"
 +
+                       
"<property><name>hive.server2.thrift.http.path</name><value>cliService</value></property>");
+    ApiConfigList hiveRoleConfigList = createMockApiConfigList(roleProperties);
+    mockClient.addResponse(ApiConfigList.class, new 
TestApiConfigListResponse(hiveRoleConfigList));
+
+    // Invoke the service discovery
+    ClouderaManagerServiceDiscovery cmsd = new 
ClouderaManagerServiceDiscovery(true);
+    ServiceDiscovery.Cluster cluster = cmsd.discover(gwConf, sdConfig, 
"test-cluster", mockClient);
+    assertNotNull(cluster);
+    assertEquals("test-cluster", cluster.getName());
+    List<String> hiveURLs = cluster.getServiceURLs("HIVE");
+    assertNotNull(hiveURLs);
+    assertEquals(1, hiveURLs.size());
+    assertEquals("http://test-host-1:10001/cliService";, hiveURLs.get(0));
+  }
+
+  private ServiceDiscoveryConfig createMockDiscoveryConfig() {
+    return createMockDiscoveryConfig("http://localhost:1234";, "itsme");
+  }
+
+  private ServiceDiscoveryConfig createMockDiscoveryConfig(String address, 
String username) {
+    ServiceDiscoveryConfig config = 
EasyMock.createNiceMock(ServiceDiscoveryConfig.class);
+    EasyMock.expect(config.getAddress()).andReturn(address).anyTimes();
+    EasyMock.expect(config.getUser()).andReturn(username).anyTimes();
+    EasyMock.expect(config.getPasswordAlias()).andReturn(null).anyTimes();
+    EasyMock.replay(config);
+    return config;
+  }
+
+  private ApiService createMockApiService(String name, String type) {
+    ApiService s = EasyMock.createNiceMock(ApiService.class);
+    EasyMock.expect(s.getName()).andReturn(name).anyTimes();
+    EasyMock.expect(s.getType()).andReturn(type).anyTimes();
+    EasyMock.replay(s);
+    return s;
+  }
+
+  private ApiRole createMockApiRole(String name, String type, String hostname) 
{
+    ApiRole r = EasyMock.createNiceMock(ApiRole.class);
+    EasyMock.expect(r.getName()).andReturn(name).anyTimes();
+    EasyMock.expect(r.getType()).andReturn(type).anyTimes();
+    ApiHostRef hostRef = EasyMock.createNiceMock(ApiHostRef.class);
+    EasyMock.expect(hostRef.getHostname()).andReturn(hostname).anyTimes();
+    EasyMock.replay(hostRef);
+    EasyMock.expect(r.getHostRef()).andReturn(hostRef).anyTimes();
+    EasyMock.replay(r);
+    return r;
+  }
+
+  private ApiServiceConfig createMockApiServiceConfig() {
+    return createMockApiServiceConfig(Collections.emptyMap());
+  }
+
+  private ApiServiceConfig createMockApiServiceConfig(Map<String, String> 
properties) {
+    ApiServiceConfig serviceConfig = 
EasyMock.createNiceMock(ApiServiceConfig.class);
+    List<ApiConfig> serviceConfigs = new ArrayList<>();
+
+    for (Map.Entry<String, String> property : properties.entrySet()) {
+      ApiConfig config = EasyMock.createNiceMock(ApiConfig.class);
+      
EasyMock.expect(config.getName()).andReturn(property.getKey()).anyTimes();
+      
EasyMock.expect(config.getValue()).andReturn(property.getValue()).anyTimes();
+      EasyMock.replay(config);
+      serviceConfigs.add(config);
+    }
+
+    
EasyMock.expect(serviceConfig.getItems()).andReturn(serviceConfigs).anyTimes();
+    EasyMock.replay(serviceConfig);
+    return serviceConfig;
+  }
+
+  private ApiConfigList createMockApiConfigList(Map<String, String> 
properties) {
+    ApiConfigList configList = EasyMock.createNiceMock(ApiConfigList.class);
+    List<ApiConfig> roleConfigs = new ArrayList<>();
+
+    for (Map.Entry<String, String> property : properties.entrySet()) {
+      ApiConfig config = EasyMock.createNiceMock(ApiConfig.class);
+      
EasyMock.expect(config.getName()).andReturn(property.getKey()).anyTimes();
+      
EasyMock.expect(config.getValue()).andReturn(property.getValue()).anyTimes();
+      EasyMock.replay(config);
+      roleConfigs.add(config);
+    }
+
+    EasyMock.expect(configList.getItems()).andReturn(roleConfigs).anyTimes();
+    EasyMock.replay(configList);
+    return configList;
+  }
+
+  private static class TestDiscoveryApiClient extends DiscoveryApiClient {
+
+    private Map<Type, ApiResponse<?>> responseMap = new HashMap<>();
+
+    TestDiscoveryApiClient(ServiceDiscoveryConfig sdConfig, AliasService 
aliasService) {
+      super(sdConfig, aliasService);
+    }
+
+    void addResponse(Type type, ApiResponse<?> response) {
+      responseMap.put(type, response);
+    }
+
+    @Override
+    boolean isKerberos() {
+      return false;
+    }
+
+    @Override
+    public <T> ApiResponse<T> execute(Call call, Type returnType) throws 
ApiException {
+      return (ApiResponse<T>) responseMap.get(returnType);
+    }
+  }
+
+  private static class TestResponseBase<T> extends ApiResponse<T> {
+    protected T data;
+
+    TestResponseBase(T data) {
+      super(200, Collections.emptyMap());
+      this.data = data;
+    }
+
+    @Override
+    public T getData() {
+      return data;
+    }
+  }
+
+  private static class TestApiServiceListResponse extends 
TestResponseBase<ApiServiceList> {
+    TestApiServiceListResponse(ApiServiceList data) {
+      super(data);
+    }
+  }
+
+  private static class TestApiServiceConfigResponse extends 
TestResponseBase<ApiServiceConfig> {
+    TestApiServiceConfigResponse(ApiServiceConfig data) {
+      super(data);
+    }
+  }
+
+  private static class TestApiRoleListResponse extends 
TestResponseBase<ApiRoleList> {
+    TestApiRoleListResponse(ApiRoleList data) {
+      super(data);
+    }
+  }
+
+  private static class TestApiConfigListResponse extends 
TestResponseBase<ApiConfigList> {
+    TestApiConfigListResponse(ApiConfigList data) {
+      super(data);
+    }
+  }
+
+}
diff --git a/gateway-release/pom.xml b/gateway-release/pom.xml
index b500844..b8f2b4a 100644
--- a/gateway-release/pom.xml
+++ b/gateway-release/pom.xml
@@ -366,6 +366,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-discovery-cm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
             <artifactId>gateway-adapter</artifactId>
         </dependency>
         <dependency>
diff --git a/pom.xml b/pom.xml
index e235ae1..a9ae1b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
         <module>gateway-i18n-logging-sl4j</module>
         <module>gateway-spi</module>
         <module>gateway-discovery-ambari</module>
+        <module>gateway-discovery-cm</module>
         <module>gateway-server</module>
         <module>gateway-server-launcher</module>
         <module>gateway-server-xforwarded-filter</module>
@@ -1049,6 +1050,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.knox</groupId>
+                <artifactId>gateway-discovery-cm</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.knox</groupId>
                 <artifactId>gateway-release</artifactId>
                 <version>${project.version}</version>
             </dependency>

Reply via email to