Browse Source

Merge branch 'trunk' into branch-alerts-dev

Jonathan Hurley 10 năm trước cách đây
mục cha
commit
f923fd880b
47 tập tin đã thay đổi với 741 bổ sung212 xóa
  1. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
  2. 15 0
      ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
  3. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html
  4. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
  5. 2 1
      ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
  6. 2 2
      ambari-admin/src/main/resources/ui/admin-web/app/views/main.html
  7. 2 1
      ambari-agent/pom.xml
  8. 4 4
      ambari-server/pom.xml
  9. 2 1
      ambari-server/src/main/assemblies/server.xml
  10. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
  11. 2 32
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
  12. 82 9
      ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java
  13. 1 2
      ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
  14. 94 45
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
  15. 21 5
      ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
  16. 1 1
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/configuration/ganglia-env.xml
  17. 3 4
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/package/scripts/params.py
  18. 1 1
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/configuration/oozie-env.xml
  19. 16 11
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/files/oozieSmoke2.sh
  20. 4 4
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/oozie.py
  21. 9 8
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/oozie_service.py
  22. 23 7
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/params.py
  23. 3 2
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/service_check.py
  24. 1 4
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/metainfo.xml
  25. 4 2
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/params.py
  26. 2 17
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java
  27. 257 9
      ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java
  28. 1 0
      ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
  29. 54 9
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java
  30. 10 1
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeTest.java
  31. 12 0
      ambari-server/src/test/python/stacks/2.0.6/OOZIE/test_oozie_server.py
  32. 2 1
      ambari-server/src/test/python/stacks/2.0.6/OOZIE/test_service_check.py
  33. 53 4
      ambari-web/app/controllers/main/service/manage_config_groups_controller.js
  34. 1 0
      ambari-web/app/controllers/wizard.js
  35. 12 2
      ambari-web/app/controllers/wizard/step7_controller.js
  36. 5 0
      ambari-web/app/models/config_group.js
  37. 1 1
      ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs
  38. 2 2
      contrib/views/jobs/src/main/resources/view.xml
  39. 2 2
      contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java
  40. 2 1
      contrib/views/slider/src/main/resources/ui/app/controllers/slider_apps_controller.js
  41. 5 6
      contrib/views/slider/src/main/resources/ui/app/controllers/slider_controller.js
  42. 4 3
      contrib/views/slider/src/main/resources/ui/app/mappers/slider_apps_mapper.js
  43. 3 0
      contrib/views/slider/src/main/resources/ui/app/templates/unavailable_apps.hbs
  44. 1 1
      contrib/views/slider/src/main/resources/ui/app/translations.js
  45. 1 1
      contrib/views/slider/src/main/resources/view.xml
  46. 14 2
      docs/pom.xml
  47. 1 0
      pom.xml

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js

@@ -23,7 +23,7 @@ angular.module('ambariAdminConsole')
     getStatus: function() {
       var deferred = $q.defer();
 
-      $http.get(Settings.baseUrl + '/clusters')
+      $http.get(Settings.baseUrl + '/clusters?fields=Clusters/provisioning_state')
       .then(function(data, status, headers) {
         deferred.resolve(data.data.items[0]);
       })

+ 15 - 0
ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css

@@ -123,6 +123,14 @@
   transition: none;
 }
 
+.cluster-installation-progress-label{
+  display: block;
+  color: #888;
+  text-align: center;
+  padding: 10px 0px;
+  cursor: default;
+}
+
 .add-item-input span{
   display: block;
   outline: none;
@@ -600,6 +608,13 @@ table.no-border tr td{
   padding: 0;
 }
 
+.property-form label{
+  width: 214px;
+  word-wrap: normal;
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+
 .views-list-pane{}
 .views-list-pane .panel-body table{
   margin-bottom: 0;

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/create.html

@@ -97,7 +97,7 @@
     <div class="panel-heading">
       <h3 class="panel-title">Properties</h3>
     </div>
-    <div class="panel-body">
+    <div class="panel-body property-form">
       <div class="form-group" ng-repeat="parameter in instance.properties"
         ng-class="{'has-error' : (form.isntanceCreateForm[parameter.name].$error.required && form.isntanceCreateForm.submitted)}" >
         <label for="" class="col-sm-3 control-label" ng-class="{'not-required': !parameter.required}" tooltip="{{parameter.description}}">{{parameter.name}}{{parameter.required ? '*' : ''}}</label>

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html

@@ -134,7 +134,7 @@
     </div>
   </div>
   <div class="panel-body">
-    <form name="propertiesForm" class="form-horizontal" ng-hide="isConfigurationEmpty" novalidate>
+    <form name="propertiesForm" class="form-horizontal property-form" ng-hide="isConfigurationEmpty" novalidate>
       <fieldset>
         <div class="form-group" ng-repeat="property in configurationMeta" ng-class="{'has-error' : property.required && propertiesForm[property.name].$error.required && !editConfigurationDisabled}" tooltip="{{property.description}}">
           <label for="" class="control-label col-sm-3" ng-class="{'not-required': !property.required}">{{property.name}}{{property.required ? '*' : ''}}</label>

+ 2 - 1
ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html

@@ -53,12 +53,13 @@
 
         </div>
 
-        <ul class="nav nav-pills nav-stacked">
+        <ul class="nav nav-pills nav-stacked" ng-show="cluster.Clusters.provisioning_state == 'INSTALLED' ">
           <li ng-class="{active: isActive('clusters.manageAccess')}">
             <a href="#/clusters/{{cluster.Clusters.cluster_name}}/manageAccess" class="permissions">Permissions</a>
           </li>
           <li><a href="/#/main/dashboard" class="gotodashboard">Go to Dashboard</a></li>
         </ul>
+        <span class="cluster-installation-progress-label" ng-show="cluster.Clusters.provisioning_state == 'INIT'">Cluster creation in progress...</span>
       </div>
         
       <div ng-hide="cluster">

+ 2 - 2
ambari-admin/src/main/resources/ui/admin-web/app/views/main.html

@@ -28,8 +28,8 @@
         <div class="description">Manage the configuration of your cluster and monitor the health of your services</div>
         <div class="glyphicon glyphicon-cloud"></div>
         <div class="buttons">
-          <span ng-class="{active: isActive('clusters.manageAccess')}"><a href="#/clusters/{{cluster.Clusters.cluster_name}}/manageAccess" class="btn btn-primary permission-button">Manage Permissions</a></span>
-          <span><a href="/#/main/dashboard" class="btn btn-primary go-dashboard-button" target="{{cluster.Clusters.cluster_name}}">Go to Dashboard</a></span>
+        <span ng-class="{active: isActive('clusters.manageAccess')}"><a href="#/clusters/{{cluster.Clusters.cluster_name}}/manageAccess" class="btn btn-primary permission-button" ng-disabled="cluster.Clusters.provisioning_state != 'INSTALLED' ">Manage Permissions</a></span>
+          <span><a href="/#/main/dashboard" class="btn btn-primary go-dashboard-button" target="{{cluster.Clusters.cluster_name}}" ng-disabled="cluster.Clusters.provisioning_state != 'INSTALLED' ">Go to Dashboard</a></span>
         </div>
       </div>
       <div ng-hide="cluster" class="col-sm-11 thumbnail">

+ 2 - 1
ambari-agent/pom.xml

@@ -583,7 +583,8 @@
                 <resource>
                   <directory>${ambari.server.module}/src/main/resources</directory>
                   <includes>
-                    <include>stacks/**/*</include>
+                    <include>stacks/stack_advisor.py</include>
+                    <include>stacks/${stack.distribution}/**/*</include>
                     <include>custom_actions/**/*</include>
                   </includes>
                   <filtering>false</filtering>

+ 4 - 4
ambari-server/pom.xml

@@ -480,10 +480,10 @@
               </sources>
             </mapping>
             <mapping>
-              <directory>/var/lib/ambari-server/resources/stacks/HDP</directory>
+              <directory>/var/lib/ambari-server/resources/stacks/${stack.distribution}</directory>
               <sources>
                 <source>
-                  <location>target/classes/stacks/HDP</location>
+                  <location>target/classes/stacks/${stack.distribution}</location>
                 </source>
               </sources>
             </mapping>
@@ -843,11 +843,11 @@
             </mapper>
           </data>
           <data>
-            <src>target/classes/stacks/HDP</src>
+            <src>target/classes/stacks/${stack.distribution}</src>
             <type>directory</type>
             <mapper>
               <type>perm</type>
-              <prefix>/var/lib/ambari-server/resources/stacks/HDP</prefix>
+              <prefix>/var/lib/ambari-server/resources/stacks/${stack.distribution}</prefix>
             </mapper>
           </data>
           <data>

+ 2 - 1
ambari-server/src/main/assemblies/server.xml

@@ -93,7 +93,8 @@
       <directory>src/main/resources</directory>
       <outputDirectory>/ambari-server-${project.version}/var/lib/ambari-server/resources/</outputDirectory>
       <includes>
-        <include>stacks/**</include>
+        <include>stacks/stack_advisor.py</include>
+        <include>stacks/${stack.distribution}/**</include>
       </includes>
     </fileSet>
   </fileSets>

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java

@@ -392,7 +392,7 @@ public class AmbariServer {
         sh.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
                     "org.apache.ambari.server.api.AmbariCsrfProtectionFilter");
         proxy.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
-                    "com.sun.jersey.api.container.filter.AmbariCsrfProtectionFilter");
+                    "org.apache.ambari.server.api.AmbariCsrfProtectionFilter");
       }
 
       //Set jetty thread pool

+ 2 - 32
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java

@@ -23,9 +23,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
@@ -47,6 +44,7 @@ import javax.persistence.UniqueConstraint;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.security.SecurityHelper;
 import org.apache.ambari.server.security.SecurityHelperImpl;
+import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
 import org.apache.ambari.server.view.configuration.InstanceConfig;
 import org.apache.ambari.view.ResourceProvider;
 import org.apache.ambari.view.ViewDefinition;
@@ -70,15 +68,6 @@ import org.apache.ambari.view.ViewInstanceDefinition;
 )
 @Entity
 public class ViewInstanceEntity implements ViewInstanceDefinition {
-  /**
-   * The prefix for every view instance context path.
-   */
-  public static final String VIEWS_CONTEXT_PATH_PREFIX = "/views/";
-
-  /**
-   * The pattern for matching view instance context path.
-   */
-  public static final String VIEWS_CONTEXT_PATH_PATTERN = "" + VIEWS_CONTEXT_PATH_PREFIX + "([^/]+)/([^/]+)/([^/]+)(.*)";
 
   @Id
   @Column(name = "view_instance_id", nullable = false)
@@ -680,26 +669,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
    * @return the context path
    */
   public static String getContextPath(String viewName, String version, String viewInstanceName) {
-    return VIEWS_CONTEXT_PATH_PREFIX + viewName + "/" + version + "/" + viewInstanceName;
-  }
-
-  /**
-   * Parses context path into view name, version and instance name
-   *
-   * @param contextPath the context path
-   * @return null if context path doesn't match correct pattern
-   */
-  public static ViewInstanceVersionDTO parseContextPath(String contextPath) {
-    final Pattern pattern = Pattern.compile(VIEWS_CONTEXT_PATH_PATTERN);
-    Matcher matcher = pattern.matcher(contextPath);
-    if (!matcher.matches()) {
-      return null;
-    } else {
-      final String viewName = matcher.group(1);
-      final String version = matcher.group(2);
-      final String instanceName = matcher.group(3);
-      return new ViewInstanceVersionDTO(viewName, version, instanceName);
-    }
+    return AmbariAuthorizationFilter.VIEWS_CONTEXT_PATH_PREFIX + viewName + "/" + version + "/" + viewInstanceName;
   }
 
   /**

+ 82 - 9
ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilter.java

@@ -19,6 +19,8 @@
 package org.apache.ambari.server.security.authorization;
 
 import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -31,7 +33,6 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
-import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
 import org.apache.ambari.server.orm.entities.ViewInstanceEntity.ViewInstanceVersionDTO;
 import org.apache.ambari.server.security.authorization.internal.InternalAuthenticationToken;
 import org.apache.ambari.server.view.ViewRegistry;
@@ -47,6 +48,21 @@ public class AmbariAuthorizationFilter implements Filter {
 
   private static final String INTERNAL_TOKEN_HEADER = "X-Internal-Token";
 
+  private static final Pattern STACK_ADVISOR_REGEX = Pattern.compile("/api/v[0-9]+/stacks/[^/]+/versions/[^/]+/validations.*");
+
+  public static final String API_VERSION_PREFIX        = "/api/v[0-9]+";
+  public static final String VIEWS_CONTEXT_PATH_PREFIX = "/views/";
+
+  private static final String VIEWS_CONTEXT_PATH_PATTERN       = VIEWS_CONTEXT_PATH_PREFIX + "([^/]+)/([^/]+)/([^/]+)(.*)";
+  private static final String VIEWS_CONTEXT_ALL_PATTERN        = VIEWS_CONTEXT_PATH_PREFIX + ".*";
+  private static final String API_USERS_USERNAME_PATTERN       = API_VERSION_PREFIX + "/users/([^/?]+)(.*)";
+  private static final String API_USERS_ALL_PATTERN            = API_VERSION_PREFIX + "/users.*";
+  private static final String API_GROUPS_ALL_PATTERN           = API_VERSION_PREFIX + "/groups.*";
+  private static final String API_CLUSTERS_ALL_PATTERN         = API_VERSION_PREFIX + "/clusters.*";
+  private static final String API_VIEWS_ALL_PATTERN            = API_VERSION_PREFIX + "/views.*";
+  private static final String API_PERSIST_ALL_PATTERN          = API_VERSION_PREFIX + "/persist.*";
+  private static final String API_LDAP_SYNC_EVENTS_ALL_PATTERN = API_VERSION_PREFIX + "/ldap_sync_events.*";
+
   /**
    * The realm to use for the basic http auth
    */
@@ -89,20 +105,26 @@ public class AmbariAuthorizationFilter implements Filter {
             break;
           }
 
-          if (requestURI.matches("/api/v[0-9]+/clusters.*")) {
             // clusters require permission
+	  if (requestURI.matches(API_CLUSTERS_ALL_PATTERN)) {
             if (permissionId.equals(PermissionEntity.CLUSTER_READ_PERMISSION) ||
                 permissionId.equals(PermissionEntity.CLUSTER_OPERATE_PERMISSION)) {
               authorized = true;
               break;
             }
-          } else if (requestURI.matches("/api/v[0-9]+/views.*")) {
+          } else if (STACK_ADVISOR_REGEX.matcher(requestURI).matches()) {
+            //TODO permissions model doesn't manage stacks api, but we need access to stack advisor to save configs
+            if (permissionId.equals(PermissionEntity.CLUSTER_OPERATE_PERMISSION)) {
+              authorized = true;
+              break;
+            }
+	  } else if (requestURI.matches(API_VIEWS_ALL_PATTERN)) {
             // views require permission
             if (permissionId.equals(PermissionEntity.VIEW_USE_PERMISSION)) {
               authorized = true;
               break;
             }
-          } else if (requestURI.matches("/api/v[0-9]+/persist.*")) {
+          } else if (requestURI.matches(API_PERSIST_ALL_PATTERN)) {
             if (permissionId.equals(PermissionEntity.CLUSTER_OPERATE_PERMISSION)) {
               authorized = true;
               break;
@@ -111,14 +133,26 @@ public class AmbariAuthorizationFilter implements Filter {
         }
       }
 
-      if (!authorized && requestURI.matches(ViewInstanceEntity.VIEWS_CONTEXT_PATH_PATTERN)) {
-        final ViewInstanceVersionDTO dto = ViewInstanceEntity.parseContextPath(requestURI);
-        authorized = ViewRegistry.getInstance().checkPermission(dto.getViewName(), dto.getVersion(), dto.getInstanceName(), true);
+      if (!authorized && requestURI.matches(VIEWS_CONTEXT_PATH_PATTERN)) {
+        final ViewInstanceVersionDTO dto = parseViewInstanceInfo(requestURI);
+        authorized = getViewRegistry().checkPermission(dto.getViewName(), dto.getVersion(), dto.getInstanceName(), true);
       }
 
-      // allow GET for everything except views
+      // allow all types of requests for /users/{current_user}
+      if (!authorized && requestURI.matches(API_USERS_USERNAME_PATTERN)) {
+        final SecurityContext securityContext = getSecurityContext();
+        final String currentUserName = securityContext.getAuthentication().getName();
+        final String urlUserName = parseUserName(requestURI);
+        authorized = currentUserName.equalsIgnoreCase(urlUserName);
+      }
+
+      // allow GET for everything except /views, /api/v1/users, /api/v1/groups, /api/v1/ldap_sync_events
       if (!authorized &&
-          (!httpRequest.getMethod().equals("GET") || requestURI.matches("/views.*"))) {
+          (!httpRequest.getMethod().equals("GET")
+              || requestURI.matches(VIEWS_CONTEXT_ALL_PATTERN)
+              || requestURI.matches(API_USERS_ALL_PATTERN)
+              || requestURI.matches(API_GROUPS_ALL_PATTERN)
+              || requestURI.matches(API_LDAP_SYNC_EVENTS_ALL_PATTERN))) {
 
         httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
         httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "You do not have permissions to access this resource.");
@@ -157,7 +191,46 @@ public class AmbariAuthorizationFilter implements Filter {
     return value == null || value.length() == 0 ? defaultValue : value;
   }
 
+  /**
+   * Parses context path into view name, version and instance name
+   *
+   * @param contextPath the context path
+   * @return null if context path doesn't match correct pattern
+   */
+  static ViewInstanceVersionDTO parseViewInstanceInfo(String contextPath) {
+    final Pattern pattern = Pattern.compile(VIEWS_CONTEXT_PATH_PATTERN);
+    final Matcher matcher = pattern.matcher(contextPath);
+    if (!matcher.matches()) {
+      return null;
+    } else {
+      final String viewName = matcher.group(1);
+      final String version = matcher.group(2);
+      final String instanceName = matcher.group(3);
+      return new ViewInstanceVersionDTO(viewName, version, instanceName);
+    }
+  }
+
+  /**
+   * Parses url to get user name.
+   *
+   * @param url the url
+   * @return null if url doesn't match correct pattern
+   */
+  static String parseUserName(String url) {
+    final Pattern pattern = Pattern.compile(API_USERS_USERNAME_PATTERN);
+    final Matcher matcher = pattern.matcher(url);
+    if (!matcher.matches()) {
+      return null;
+    } else {
+      return matcher.group(1);
+    }
+  }
+
   SecurityContext getSecurityContext() {
     return SecurityContextHolder.getContext();
   }
+
+  ViewRegistry getViewRegistry() {
+    return ViewRegistry.getInstance();
+  }
 }

+ 1 - 2
ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java

@@ -24,7 +24,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.AmbariException;
@@ -66,7 +65,7 @@ import com.google.inject.persist.Transactional;
 @Singleton
 public class Users {
 
-  private final static Logger LOG = LoggerFactory.getLogger(Users.class);
+  private static final Logger LOG = LoggerFactory.getLogger(Users.class);
 
   @Inject
   Provider<EntityManager> entityManagerProvider;

+ 94 - 45
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java

@@ -46,6 +46,7 @@ import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntityPK;
+import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
@@ -71,6 +72,7 @@ import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.alert.Scope;
 import org.apache.ambari.server.utils.StageUtils;
+import org.apache.ambari.server.view.ViewRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -116,9 +118,16 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
   private static final String ALERT_TABLE_GROUPING = "alert_grouping";
   private static final String ALERT_TABLE_NOTICE = "alert_notice";
   public static final String JOBS_VIEW_NAME = "JOBS";
+  public static final String VIEW_NAME_REG_EXP = JOBS_VIEW_NAME + "\\{.*\\}";
   public static final String JOBS_VIEW_INSTANCE_NAME = "JOBS_1";
   public static final String SHOW_JOBS_FOR_NON_ADMIN_KEY = "showJobsForNonAdmin";
   public static final String JOBS_VIEW_INSTANCE_LABEL = "Jobs";
+  public static final String CLUSTER_STATE_STACK_HDP_2_1 = "{\"stackName\":\"HDP\",\"stackVersion\":\"2.1\"}";
+  public static final String YARN_TIMELINE_SERVICE_WEBAPP_ADDRESS_PROPERTY = "yarn.timeline-service.webapp.address";
+  public static final String YARN_RESOURCEMANAGER_WEBAPP_ADDRESS_PROPERTY = "yarn.resourcemanager.webapp.address";
+  public static final String YARN_SITE = "yarn-site";
+  public static final String YARN_ATS_URL_PROPERTY = "yarn.ats.url";
+  public static final String YARN_RESOURCEMANAGER_URL_PROPERTY = "yarn.resourcemanager.url";
 
   //SourceVersion is only for book-keeping purpos
   @Override
@@ -1296,56 +1305,96 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
     final KeyValueDAO keyValueDAO = injector.getInstance(KeyValueDAO.class);
     final PermissionDAO permissionDAO = injector.getInstance(PermissionDAO.class);
     final PrivilegeDAO privilegeDAO = injector.getInstance(PrivilegeDAO.class);
-
-    ViewEntity jobsView = viewDAO.findByCommonName(JOBS_VIEW_NAME);
-    if (jobsView != null) {
-      ViewInstanceEntity jobsInstance = jobsView.getInstanceDefinition(JOBS_VIEW_INSTANCE_NAME);
-      if (jobsInstance == null) {
-        jobsInstance = new ViewInstanceEntity(jobsView, JOBS_VIEW_INSTANCE_NAME, JOBS_VIEW_INSTANCE_LABEL);
-        ResourceEntity resourceEntity = new ResourceEntity();
-        resourceEntity.setResourceType(resourceTypeDAO.findByName(
-            ViewEntity.getViewName(
-                jobsView.getCommonName(),
-                jobsView.getVersion())));
-        jobsInstance.setResource(resourceEntity);
-        jobsView.addInstanceDefinition(jobsInstance);
-        resourceDAO.create(resourceEntity);
-        viewInstanceDAO.create(jobsInstance);
-        viewDAO.merge(jobsView);
-      }
-      // get showJobsForNonAdmin value and remove it
-      boolean showJobsForNonAdmin = false;
-      KeyValueEntity showJobsKeyValueEntity = keyValueDAO.findByKey(SHOW_JOBS_FOR_NON_ADMIN_KEY);
-      if (showJobsKeyValueEntity != null) {
-        String value = showJobsKeyValueEntity.getValue();
-        showJobsForNonAdmin = Boolean.parseBoolean(value);
-        keyValueDAO.remove(showJobsKeyValueEntity);
-      }
-      if (showJobsForNonAdmin) {
-        ResourceEntity jobsResource = jobsInstance.getResource();
-        PermissionEntity viewUsePermission = permissionDAO.findViewUsePermission();
-        for (UserEntity userEntity : userDAO.findAll()) {
-          // check if user has VIEW.USE privilege for JOBS view
-          List<PrivilegeEntity> privilegeEntities = privilegeDAO.findAllByPrincipal(
-              Collections.singletonList(userEntity.getPrincipal()));
-          boolean hasJobsUsePrivilege = false;
-          for (PrivilegeEntity privilegeEntity : privilegeEntities) {
-            if (privilegeEntity.getResource().getId() == jobsInstance.getResource().getId() &&
-                privilegeEntity.getPermission().getId() == viewUsePermission.getId()) {
-              hasJobsUsePrivilege = true;
-              break;
+    final ClusterDAO clusterDAO = injector.getInstance(ClusterDAO.class);
+    final ViewRegistry viewRegistry = injector.getInstance(ViewRegistry.class);
+
+    List<ClusterEntity> clusters = clusterDAO.findAll();
+    if (!clusters.isEmpty()) {
+      ClusterEntity currentCluster = clusters.get(0);
+      String currentStackVersion = currentCluster.getClusterStateEntity().getCurrentStackVersion();
+      if (CLUSTER_STATE_STACK_HDP_2_1.equals(currentStackVersion)) {
+        ViewRegistry.initInstance(viewRegistry);
+        viewRegistry.readViewArchives(VIEW_NAME_REG_EXP);
+        ViewEntity jobsView = viewDAO.findByCommonName(JOBS_VIEW_NAME);
+
+        if (jobsView != null) {
+          ViewInstanceEntity jobsInstance = jobsView.getInstanceDefinition(JOBS_VIEW_INSTANCE_NAME);
+          if (jobsInstance == null) {
+            jobsInstance = new ViewInstanceEntity(jobsView, JOBS_VIEW_INSTANCE_NAME, JOBS_VIEW_INSTANCE_LABEL);
+            ResourceEntity resourceEntity = new ResourceEntity();
+            resourceEntity.setResourceType(resourceTypeDAO.findByName(
+                ViewEntity.getViewName(
+                    jobsView.getCommonName(),
+                    jobsView.getVersion())));
+            String atsHost;
+            String rmHost;
+            try {
+              ClusterConfigEntity currentYarnConfig = null;
+              for (ClusterConfigMappingEntity configMappingEntity : currentCluster.getConfigMappingEntities()) {
+                if (YARN_SITE.equals(configMappingEntity.getType()) && configMappingEntity.isSelected() > 0) {
+                  currentYarnConfig = clusterDAO.findConfig(currentCluster.getClusterId(),
+                      configMappingEntity.getType(), configMappingEntity.getTag());
+                  break;
+                }
+              }
+              Type type = new TypeToken<Map<String, String>>() {}.getType();
+              Map<String, String> yarnSiteProps = StageUtils.getGson().fromJson(currentYarnConfig.getData(), type);
+              atsHost = yarnSiteProps.get(YARN_TIMELINE_SERVICE_WEBAPP_ADDRESS_PROPERTY);
+              rmHost = yarnSiteProps.get(YARN_RESOURCEMANAGER_WEBAPP_ADDRESS_PROPERTY);
+            } catch (Exception ex) {
+              // Required properties failed to be set, therefore jobs instance should not be created
+              return;
             }
+            jobsInstance.setResource(resourceEntity);
+            jobsInstance.putProperty(YARN_ATS_URL_PROPERTY, "http://" + atsHost);
+            jobsInstance.putProperty(YARN_RESOURCEMANAGER_URL_PROPERTY, "http://" + rmHost);
+            jobsView.addInstanceDefinition(jobsInstance);
+            resourceDAO.create(resourceEntity);
+            viewInstanceDAO.create(jobsInstance);
+            viewDAO.merge(jobsView);
           }
-          // if not - add VIEW.use privilege
-          if (!hasJobsUsePrivilege) {
-            PrivilegeEntity privilegeEntity = new PrivilegeEntity();
-            privilegeEntity.setResource(jobsResource);
-            privilegeEntity.setPermission(viewUsePermission);
-            privilegeEntity.setPrincipal(userEntity.getPrincipal());
-            privilegeDAO.create(privilegeEntity);
+          // get showJobsForNonAdmin value and remove it
+          boolean showJobsForNonAdmin = false;
+          KeyValueEntity showJobsKeyValueEntity = keyValueDAO.findByKey(SHOW_JOBS_FOR_NON_ADMIN_KEY);
+          if (showJobsKeyValueEntity != null) {
+            String value = showJobsKeyValueEntity.getValue();
+            showJobsForNonAdmin = Boolean.parseBoolean(value);
+            keyValueDAO.remove(showJobsKeyValueEntity);
+          }
+          if (showJobsForNonAdmin) {
+            ResourceEntity jobsResource = jobsInstance.getResource();
+            long jobsResourceId = jobsResource.getId();
+            PermissionEntity viewUsePermission = permissionDAO.findViewUsePermission();
+            PermissionEntity adminPermission = permissionDAO.findAmbariAdminPermission();
+            int viewUsePermissionId = viewUsePermission.getId();
+            int adminPermissionId = adminPermission.getId();
+            for (UserEntity userEntity : userDAO.findAll()) {
+              // check if user has VIEW.USE privilege for JOBS view
+              List<PrivilegeEntity> privilegeEntities = privilegeDAO.findAllByPrincipal(
+                  Collections.singletonList(userEntity.getPrincipal()));
+              boolean hasJobsUsePrivilege = false;
+              for (PrivilegeEntity privilegeEntity : privilegeEntities) {
+                int privilegePermissionId = privilegeEntity.getPermission().getId();
+                Long privilegeResourceId = privilegeEntity.getResource().getId();
+                if ((privilegePermissionId == viewUsePermissionId && privilegeResourceId == jobsResourceId)
+                    || privilegePermissionId == adminPermissionId) {
+                  hasJobsUsePrivilege = true;
+                  break;
+                }
+              }
+              // if not - add VIEW.use privilege
+              if (!hasJobsUsePrivilege) {
+                PrivilegeEntity privilegeEntity = new PrivilegeEntity();
+                privilegeEntity.setResource(jobsResource);
+                privilegeEntity.setPermission(viewUsePermission);
+                privilegeEntity.setPrincipal(userEntity.getPrincipal());
+                privilegeDAO.create(privilegeEntity);
+              }
+            }
           }
         }
       }
     }
+
   }
 }

+ 21 - 5
ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java

@@ -102,6 +102,7 @@ public class ViewRegistry {
    */
   private static final String EXTRACTED_ARCHIVES_DIR = "work";
   private static final String EXTRACT_COMMAND = "extract";
+  private static final String ALL_VIEWS_REG_EXP = ".*";
 
   /**
    * Thread pool
@@ -424,10 +425,19 @@ public class ViewRegistry {
   }
 
   /**
-   * Read the view archives.
+   * Read all view archives.
    */
   public void readViewArchives() {
-    readViewArchives(false, true);
+    readViewArchives(false, true, ALL_VIEWS_REG_EXP, true);
+  }
+
+  /**
+   * Read only view archives with names corresponding to given regular expression.
+   *
+   * @param viewNameRegExp view name regular expression
+   */
+  public void readViewArchives(String viewNameRegExp) {
+    readViewArchives(false, false, viewNameRegExp, false);
   }
 
   /**
@@ -1147,9 +1157,9 @@ public class ViewRegistry {
     }
   }
 
-
   // read the view archives.
-  private void readViewArchives(boolean systemOnly, boolean useExecutor) {
+  private void readViewArchives(boolean systemOnly, boolean useExecutor,
+                                String viewNameRegExp, boolean removeUndeployed) {
     try {
       File viewDir = configuration.getViewsDir();
 
@@ -1173,6 +1183,10 @@ public class ViewRegistry {
               String version    = viewConfig.getVersion();
               String viewName   = ViewEntity.getViewName(commonName, version);
 
+              if (!viewName.matches(viewNameRegExp)) {
+                continue;
+              }
+
               final String extractedArchiveDirPath = extractedArchivesPath + File.separator + viewName;
               final File extractedArchiveDirFile = archiveUtility.getFile(extractedArchiveDirPath);
 
@@ -1210,7 +1224,9 @@ public class ViewRegistry {
             }
           }
 
-          removeUndeployedViews();
+          if (removeUndeployed) {
+            removeUndeployedViews();
+          }
         }
       } else {
         LOG.error("Could not create extracted view archive directory " + extractedArchivesPath + ".");

+ 1 - 1
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/configuration/ganglia-env.xml

@@ -71,7 +71,7 @@
   <property>
     <name>additional_clusters</name>
     <value> </value>
-    <description>Add additional desired Ganglia metrics cluster in the form "name1,port1,name2,port2". Ensure that the names and ports are unique across all cluster and ports are available on ganglia server host. Ambari has reserved ports 8667-8669 within its own pool.</description>
+    <description>Add additional desired Ganglia metrics cluster in the form "name1:port1,name2:port2". Ensure that the names and ports are unique across all cluster and ports are available on ganglia server host. Ambari has reserved ports 8667-8669 within its own pool.</description>
   </property>
 
 </configuration>

+ 3 - 4
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/package/scripts/params.py

@@ -38,10 +38,9 @@ if gmond_add_clusters_str and gmond_add_clusters_str.isspace():
 gmond_app_strs = [] if gmond_add_clusters_str is None else gmond_add_clusters_str.split(',')
 gmond_apps = []
 
-i = 0
-while i < len(gmond_app_strs):
-  gmond_apps.append((gmond_app_strs[i].strip(), gmond_app_strs[i+1].strip()))
-  i = i + 2
+for x in gmond_app_strs:
+  a,b = x.strip().split(':')
+  gmond_apps.append((a.strip(),b.strip()))
 
 if System.get_instance().os_family == "ubuntu":
   gmond_service_name = "ganglia-monitor"

+ 1 - 1
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/configuration/oozie-env.xml

@@ -67,7 +67,7 @@
 
 if [ -d "/usr/lib/bigtop-tomcat" ]; then
   export OOZIE_CONFIG=${OOZIE_CONFIG:-/etc/oozie/conf}
-  export CATALINA_BASE=${CATALINA_BASE:-/var/lib/oozie/oozie-server}
+  export CATALINA_BASE=${CATALINA_BASE:-{{oozie_server_dir}}}
   export CATALINA_TMPDIR=${CATALINA_TMPDIR:-/var/tmp/oozie}
   export OOZIE_CATALINA_HOME=/usr/lib/bigtop-tomcat
 fi

+ 16 - 11
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/files/oozieSmoke2.sh

@@ -35,7 +35,7 @@ function checkOozieJobStatus {
   num_of_tries=${num_of_tries:-10}
   local i=0
   local rc=1
-  local cmd="source ${oozie_conf_dir}/oozie-env.sh ; /usr/bin/oozie job -oozie ${OOZIE_SERVER} -info $job_id"
+  local cmd="source ${oozie_conf_dir}/oozie-env.sh ; ${oozie_bin_dir}/oozie job -oozie ${OOZIE_SERVER} -info $job_id"
   su - ${smoke_test_user} -c "$cmd"
   while [ $i -lt $num_of_tries ] ; do
     cmd_output=`su - ${smoke_test_user} -c "$cmd"`
@@ -58,11 +58,13 @@ function checkOozieJobStatus {
 }
 
 export oozie_conf_dir=$1
-export hadoop_conf_dir=$2
-export smoke_test_user=$3
-export security_enabled=$4
-export smoke_user_keytab=$5
-export kinit_path_local=$6
+export oozie_bin_dir=$2
+export hadoop_conf_dir=$3
+export hadoop_bin_dir=$4
+export smoke_test_user=$5
+export security_enabled=$6
+export smoke_user_keytab=$7
+export kinit_path_local=$8
 
 export OOZIE_EXIT_CODE=0
 export JOBTRACKER=`getValueFromField ${hadoop_conf_dir}/yarn-site.xml yarn.resourcemanager.address`
@@ -77,6 +79,9 @@ fi
   
 
 export OOZIE_EXAMPLES_DIR=`$LIST_PACKAGE_FILES_CMD oozie-client | grep 'oozie-examples.tar.gz$' | xargs dirname`
+if [[ -z "$OOZIE_EXAMPLES_DIR" ]] ; then
+  export OOZIE_EXAMPLES_DIR='/usr/hdp/current/oozie/doc/'
+fi
 cd $OOZIE_EXAMPLES_DIR
 
 tar -zxf oozie-examples.tar.gz
@@ -93,12 +98,12 @@ else
   kinitcmd=""
 fi
 
-su - ${smoke_test_user} -c "hdfs --config ${hadoop_conf_dir} dfs -rm -r examples"
-su - ${smoke_test_user} -c "hdfs --config ${hadoop_conf_dir} dfs -rm -r input-data"
-su - ${smoke_test_user} -c "hdfs --config ${hadoop_conf_dir} dfs -copyFromLocal $OOZIE_EXAMPLES_DIR/examples examples"
-su - ${smoke_test_user} -c "hdfs --config ${hadoop_conf_dir} dfs -copyFromLocal $OOZIE_EXAMPLES_DIR/examples/input-data input-data"
+su - ${smoke_test_user} -c "${hadoop_bin_dir}/hdfs --config ${hadoop_conf_dir} dfs -rm -r examples"
+su - ${smoke_test_user} -c "${hadoop_bin_dir}/hdfs --config ${hadoop_conf_dir} dfs -rm -r input-data"
+su - ${smoke_test_user} -c "${hadoop_bin_dir}/hdfs --config ${hadoop_conf_dir} dfs -copyFromLocal $OOZIE_EXAMPLES_DIR/examples examples"
+su - ${smoke_test_user} -c "${hadoop_bin_dir}/hdfs --config ${hadoop_conf_dir} dfs -copyFromLocal $OOZIE_EXAMPLES_DIR/examples/input-data input-data"
 
-cmd="${kinitcmd}source ${oozie_conf_dir}/oozie-env.sh ; /usr/bin/oozie -Doozie.auth.token.cache=false job -oozie $OOZIE_SERVER -config $OOZIE_EXAMPLES_DIR/examples/apps/map-reduce/job.properties  -run"
+cmd="${kinitcmd}source ${oozie_conf_dir}/oozie-env.sh ; ${oozie_bin_dir}/oozie -Doozie.auth.token.cache=false job -oozie $OOZIE_SERVER -config $OOZIE_EXAMPLES_DIR/examples/apps/map-reduce/job.properties  -run"
 echo $cmd
 job_info=`su - ${smoke_test_user} -c "$cmd" | grep "job:"`
 job_id="`echo $job_info | cut -d':' -f2`"

+ 4 - 4
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/oozie.py

@@ -121,18 +121,18 @@ def oozie_server_specific(
     not_if="ls {pid_file} >/dev/null 2>&1 && !(ps `cat {pid_file}` >/dev/null 2>&1)"
   )
   
-  oozie_server_directorties = [params.oozie_pid_dir, params.oozie_log_dir, params.oozie_tmp_dir, params.oozie_data_dir, params.oozie_lib_dir, params.oozie_webapps_dir]            
+  oozie_server_directorties = [params.oozie_pid_dir, params.oozie_log_dir, params.oozie_tmp_dir, params.oozie_data_dir, params.oozie_lib_dir, params.oozie_webapps_dir, params.oozie_webapps_conf_dir]
   Directory( oozie_server_directorties,
     owner = params.oozie_user,
     mode = 0755,
     recursive = True
   )
 
-  cmd1 = "cd /usr/lib/oozie && tar -xvf oozie-sharelib.tar.gz"
-  cmd2 =  format("cd /usr/lib/oozie && mkdir -p {oozie_tmp_dir}")
+  cmd1 = format("cd {oozie_home} && tar -xvf oozie-sharelib.tar.gz")
+  cmd2 = format("cd {oozie_home} && mkdir -p {oozie_tmp_dir}")
   
   # this is different for HDP1
-  cmd3 = format("cd /usr/lib/oozie && chown {oozie_user}:{user_group} {oozie_tmp_dir} && mkdir -p {oozie_libext_dir} && cp {ext_js_path} {oozie_libext_dir}")
+  cmd3 = format("cd {oozie_home} && chown {oozie_user}:{user_group} {oozie_tmp_dir} && mkdir -p {oozie_libext_dir} && cp {ext_js_path} {oozie_libext_dir}")
   if params.jdbc_driver_name=="com.mysql.jdbc.Driver" or params.jdbc_driver_name=="oracle.jdbc.driver.OracleDriver":
     cmd3 += format(" && cp {jdbc_driver_jar} {oozie_libext_dir}")
   #falcon el extension

+ 9 - 8
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/oozie_service.py

@@ -27,7 +27,7 @@ def oozie_service(action = 'start'): # 'start' or 'stop'
   no_op_test = format("ls {pid_file} >/dev/null 2>&1 && ps `cat {pid_file}` >/dev/null 2>&1")
   
   if action == 'start':
-    start_cmd = format("cd {oozie_tmp_dir} && /usr/lib/oozie/bin/oozie-start.sh")
+    start_cmd = format("cd {oozie_tmp_dir} && {oozie_home}/bin/oozie-start.sh")
     
     if params.jdbc_driver_name == "com.mysql.jdbc.Driver" or \
        params.jdbc_driver_name == "org.postgresql.Driver" or \
@@ -36,15 +36,15 @@ def oozie_service(action = 'start'): # 'start' or 'stop'
     else:
       db_connection_check_command = None
       
-    cmd1 =  format("cd {oozie_tmp_dir} && /usr/lib/oozie/bin/ooziedb.sh create -sqlfile oozie.sql -run")
+    cmd1 =  format("cd {oozie_tmp_dir} && {oozie_home}/bin/ooziedb.sh create -sqlfile oozie.sql -run")
     cmd2 =  format("{kinit_if_needed} {put_shared_lib_to_hdfs_cmd} ; hadoop --config {hadoop_conf_dir} dfs -chmod -R 755 {oozie_hdfs_user_dir}/share")
 
     if not os.path.isfile(params.jdbc_driver_jar) and params.jdbc_driver_name == "org.postgresql.Driver":
       print "ERROR: jdbc file " + params.jdbc_driver_jar + " is unavailable. Please, follow next steps:\n" \
-        "1) Download postgresql-9.0-801.jdbc4.jar.\n2) Create needed directory: mkdir -p /usr/lib/oozie/libserver/\n" \
+        "1) Download postgresql-9.0-801.jdbc4.jar.\n2) Create needed directory: mkdir -p {oozie_home}/libserver/\n" \
         "3) Copy postgresql-9.0-801.jdbc4.jar to newly created dir: cp /path/to/jdbc/postgresql-9.0-801.jdbc4.jar " \
-        "/usr/lib/oozie/libserver/\n4) Copy postgresql-9.0-801.jdbc4.jar to libext: cp " \
-        "/path/to/jdbc/postgresql-9.0-801.jdbc4.jar /usr/lib/oozie/libext/\n"
+        "{oozie_home}/libserver/\n4) Copy postgresql-9.0-801.jdbc4.jar to libext: cp " \
+        "/path/to/jdbc/postgresql-9.0-801.jdbc4.jar {oozie_home}/libext/\n"
       exit(1)
 
     if db_connection_check_command:
@@ -57,8 +57,9 @@ def oozie_service(action = 'start'): # 'start' or 'stop'
     ) 
     
     Execute( cmd2,
-      user = params.oozie_user,       
-      not_if = format("{kinit_if_needed} hadoop --config {hadoop_conf_dir} dfs -ls /user/oozie/share | awk 'BEGIN {{count=0;}} /share/ {{count++}} END {{if (count > 0) {{exit 0}} else {{exit 1}}}}'")
+      user = params.oozie_user,
+      not_if = format("{kinit_if_needed} hadoop --config {hadoop_conf_dir} dfs -ls /user/oozie/share | awk 'BEGIN {{count=0;}} /share/ {{count++}} END {{if (count > 0) {{exit 0}} else {{exit 1}}}}'"),
+      path = params.execute_path
     )
     
     Execute( start_cmd,
@@ -66,7 +67,7 @@ def oozie_service(action = 'start'): # 'start' or 'stop'
       not_if  = no_op_test,
     )
   elif action == 'stop':
-    stop_cmd  = format("su - {oozie_user} -c  'cd {oozie_tmp_dir} && /usr/lib/oozie/bin/oozie-stop.sh' && rm -f {pid_file}")
+    stop_cmd  = format("su - {oozie_user} -c  'cd {oozie_tmp_dir} && {oozie_home}/bin/oozie-stop.sh' && rm -f {pid_file}")
     Execute( stop_cmd,
       only_if  = no_op_test
     )

+ 23 - 7
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/params.py

@@ -20,6 +20,7 @@ limitations under the License.
 
 from resource_management import *
 import status_params
+import os
 
 # server configurations
 config = Script.get_config()
@@ -33,14 +34,35 @@ if rpm_version is not None:
   hadoop_bin_dir = "/usr/hdp/current/hadoop/bin"
   hadoop_lib_home = "/usr/hdp/current/hadoop/lib"
   mapreduce_libs_path = "/usr/hdp/current/hadoop-mapreduce/*"
+  oozie_lib_dir = "/usr/hdp/current/oozie/"
+  oozie_setup_sh = "/usr/hdp/current/oozie/bin/oozie-setup.sh"
+  oozie_webapps_dir = "/usr/hdp/current/oozie/oozie-server/webapps"
+  oozie_webapps_conf_dir = "/usr/hdp/current/oozie/oozie-server/conf"
+  oozie_libext_dir = "/usr/hdp/current/oozie/libext"
+  oozie_server_dir = "/usr/hdp/current/oozie/oozie-server"
+  oozie_shared_lib = "/usr/hdp/current/oozie/share"
+  oozie_home = "/usr/hdp/current/oozie"
+  oozie_bin_dir = "/usr/hdp/current/oozie/bin"
+  falcon_home = '/usr/hdp/current/falcon'
 else:
   hadoop_bin_dir = "/usr/bin"
   hadoop_lib_home = "/usr/lib/hadoop/lib"
   mapreduce_libs_path = "/usr/lib/hadoop-mapreduce/*"
+  oozie_lib_dir = "/var/lib/oozie/"
+  oozie_setup_sh = "/usr/lib/oozie/bin/oozie-setup.sh"
+  oozie_webapps_dir = "/var/lib/oozie/oozie-server/webapps/"
+  oozie_webapps_conf_dir = "/var/lib/oozie/oozie-server/conf"
+  oozie_libext_dir = "/usr/lib/oozie/libext"
+  oozie_server_dir = "/var/lib/oozie/oozie-server"
+  oozie_shared_lib = "/usr/lib/oozie/share"
+  oozie_home = "/usr/lib/oozie"
+  oozie_bin_dir = "/usr/bin"
+  falcon_home = '/usr/lib/falcon'
+
+execute_path = oozie_bin_dir + os.pathsep + hadoop_bin_dir
 
 hadoop_conf_dir = "/etc/hadoop/conf"
 conf_dir = "/etc/oozie/conf"
-
 oozie_user = config['configurations']['oozie-env']['oozie_user']
 smokeuser = config['configurations']['cluster-env']['smokeuser']
 user_group = config['configurations']['cluster-env']['user_group']
@@ -55,7 +77,6 @@ hadoop_jar_location = "/usr/lib/hadoop/"
 hdp_stack_version = config['hostLevelParams']['stack_version']
 # for HDP1 it's "/usr/share/HDP-oozie/ext.zip"
 ext_js_path = "/usr/share/HDP-oozie/ext-2.2.zip"
-oozie_libext_dir = "/usr/lib/oozie/libext"
 security_enabled = config['configurations']['cluster-env']['security_enabled']
 
 kinit_path_local = functions.get_kinit_path(["/usr/bin", "/usr/kerberos/bin", "/usr/sbin"])
@@ -76,10 +97,6 @@ oozie_data_dir = config['configurations']['oozie-env']['oozie_data_dir']
 oozie_server_port = get_port_from_url(config['configurations']['oozie-site']['oozie.base.url'])
 oozie_server_admin_port = config['configurations']['oozie-env']['oozie_admin_port']
 oozie_env_sh_template = config['configurations']['oozie-env']['content']
-oozie_lib_dir = "/var/lib/oozie/"
-oozie_webapps_dir = "/var/lib/oozie/oozie-server/webapps/"
-oozie_setup_sh = "/usr/lib/oozie/bin/oozie-setup.sh"
-oozie_shared_lib = "/usr/lib/oozie/share"
 fs_root = config['configurations']['core-site']['fs.defaultFS']
 
 if str(hdp_stack_version).startswith('2.0') or str(hdp_stack_version).startswith('2.1'):
@@ -103,7 +120,6 @@ hostname = config["hostname"]
 ambari_server_hostname = config['clusterHostInfo']['ambari_server_host'][0]
 falcon_host = default("/clusterHostInfo/falcon_server_hosts", [])
 has_falcon_host = not len(falcon_host)  == 0
-falcon_home = '/usr/lib/falcon'
 
 #oozie-log4j.properties
 if (('oozie-log4j' in config['configurations']) and ('content' in config['configurations']['oozie-log4j'])):

+ 3 - 2
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/package/scripts/service_check.py

@@ -43,12 +43,13 @@ def oozie_smoke_shell_file(
   os_family = System.get_instance().os_family
   
   if params.security_enabled:
-    sh_cmd = format("{tmp_dir}/{file_name} {os_family} {conf_dir} {hadoop_conf_dir} {smokeuser} {security_enabled} {smokeuser_keytab} {kinit_path_local}")
+    sh_cmd = format("{tmp_dir}/{file_name} {os_family} {conf_dir} {oozie_bin_dir} {hadoop_conf_dir} {hadoop_bin_dir} {smokeuser} {security_enabled} {smokeuser_keytab} {kinit_path_local}")
   else:
-    sh_cmd = format("{tmp_dir}/{file_name} {os_family} {conf_dir} {hadoop_conf_dir} {smokeuser} {security_enabled}")
+    sh_cmd = format("{tmp_dir}/{file_name} {os_family} {conf_dir} {oozie_bin_dir} {hadoop_conf_dir} {hadoop_bin_dir} {smokeuser} {security_enabled}")
 
   Execute( format("{tmp_dir}/{file_name}"),
     command   = sh_cmd,
+    path      = params.execute_path,
     tries     = 3,
     try_sleep = 5,
     logoutput = True

+ 1 - 4
ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/metainfo.xml

@@ -22,7 +22,7 @@
       <name>SLIDER</name>
       <displayName>Slider</displayName>
       <comment>Apache Slider is a YARN application to deploy existing distributed applications on YARN, monitor them and make them larger or smaller as desired -even while the application is running.</comment>
-      <version>0.51.0</version>
+      <version>0.60.0.2.2.0.0</version>
       <components>
         <component>
           <name>SLIDER</name>
@@ -78,9 +78,6 @@
       <configuration-dependencies>
         <config-type>slider-log4j</config-type>
         <config-type>slider-client</config-type>
-        <config-type>hdfs-site</config-type>
-        <config-type>yarn-site</config-type>
-        <config-type>core-site</config-type>
       </configuration-dependencies>
 
     </service>

+ 4 - 2
ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/params.py

@@ -28,8 +28,10 @@ rpm_version = default("/configurations/hadoop-env/rpm_version", None)
 
 #hadoop params
 if rpm_version is not None:
-  slider_conf_dir = '/usr/lib/current/slider/conf'
-  slider_bin_dir = '/usr/lib/current/slider/bin'
+#  slider_conf_dir = '/usr/lib/current/slider/conf'
+#  slider_bin_dir = '/usr/lib/current/slider/bin'
+  slider_conf_dir = "/usr/lib/slider/conf"
+  slider_bin_dir = "/usr/lib/slider/bin"
 else:
   slider_conf_dir = "/usr/lib/slider/conf"
   slider_bin_dir = "/usr/lib/slider/bin"

+ 2 - 17
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java

@@ -20,8 +20,8 @@ package org.apache.ambari.server.orm.entities;
 
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.ambari.server.orm.entities.ViewInstanceEntity.ViewInstanceVersionDTO;
 import org.apache.ambari.server.security.SecurityHelper;
+import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
 import org.apache.ambari.server.view.ViewRegistryTest;
 import org.apache.ambari.server.view.configuration.InstanceConfig;
 import org.apache.ambari.server.view.configuration.InstanceConfigTest;
@@ -295,25 +295,10 @@ public class ViewInstanceEntityTest {
   public void testContextPath() throws Exception {
     ViewInstanceEntity viewInstanceDefinition = getViewInstanceEntity();
 
-    Assert.assertEquals(ViewInstanceEntity.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1",
+    Assert.assertEquals(AmbariAuthorizationFilter.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1",
         viewInstanceDefinition.getContextPath());
   }
 
-  @Test
-  public void testParseContextPath() throws Exception {
-    final String[] pathesToTest = {
-        ViewInstanceEntity.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1",
-        ViewInstanceEntity.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1/index.html",
-        ViewInstanceEntity.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1/api/test"
-    };
-    for (String contextPath: pathesToTest) {
-      final ViewInstanceVersionDTO dto = ViewInstanceEntity.parseContextPath(contextPath);
-      Assert.assertEquals("INSTANCE1", dto.getInstanceName());
-      Assert.assertEquals("MY_VIEW", dto.getViewName());
-      Assert.assertEquals("1.0.0", dto.getVersion());
-    }
-  }
-
   @Test
   public void testInstanceData() throws Exception {
     TestSecurityHelper securityHelper = new TestSecurityHelper("user1");

+ 257 - 9
ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariAuthorizationFilterTest.java

@@ -18,23 +18,40 @@
 
 package org.apache.ambari.server.security.authorization;
 
+import static org.easymock.EasyMock.createMockBuilder;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.Assert;
+
 import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.ViewInstanceEntity.ViewInstanceVersionDTO;
+import org.apache.ambari.server.view.ViewRegistry;
 import org.easymock.EasyMock;
-import org.junit.BeforeClass;
+import org.easymock.IAnswer;
 import org.junit.Test;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.util.Collection;
-import java.util.Collections;
-
-import static org.easymock.EasyMock.*;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
 
 public class AmbariAuthorizationFilterTest {
 
@@ -121,4 +138,235 @@ public class AmbariAuthorizationFilterTest {
     verify(request, response, chain, filter, securityContext, authentication, authority,
         privilegeEntity, permission, filterConfig);
   }
+
+  @Test
+  public void testDoFilter_adminAccess() throws Exception {
+    final Table<String, String, Boolean> urlTests = HashBasedTable.create();
+    urlTests.put("/api/v1/clusters/cluster", "GET",  true);
+    urlTests.put("/api/v1/clusters/cluster", "POST",  true);
+    urlTests.put("/api/v1/views", "GET", true);
+    urlTests.put("/api/v1/views", "POST", true);
+    urlTests.put("/api/v1/persist/SomeValue", "GET", true);
+    urlTests.put("/api/v1/persist/SomeValue", "POST", true);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "GET", true);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "POST", true);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "GET", true);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "POST", true);
+    urlTests.put("/api/v1/users/user1", "GET", true);
+    urlTests.put("/api/v1/users/user1", "POST", true);
+    urlTests.put("/api/v1/users/user2", "GET", true);
+    urlTests.put("/api/v1/users/user2", "POST", true);
+    urlTests.put("/api/v1/groups", "GET", true);
+    urlTests.put("/api/v1/ldap_sync_events", "GET", true);
+    urlTests.put("/any/other/URL", "GET", true);
+    urlTests.put("/any/other/URL", "POST", true);
+
+    performGeneralDoFilterTest("admin", new int[] {PermissionEntity.AMBARI_ADMIN_PERMISSION}, urlTests);
+  }
+
+  @Test
+  public void testDoFilter_clusterViewerAccess() throws Exception {
+    final Table<String, String, Boolean> urlTests = HashBasedTable.create();
+    urlTests.put("/api/v1/clusters/cluster", "GET",  true);
+    urlTests.put("/api/v1/clusters/cluster", "POST",  true);
+    urlTests.put("/api/v1/views", "GET", true);
+    urlTests.put("/api/v1/views", "POST", false);
+    urlTests.put("/api/v1/persist/SomeValue", "GET", true);
+    urlTests.put("/api/v1/persist/SomeValue", "POST", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "GET", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "POST", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "GET", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "POST", false);
+    urlTests.put("/api/v1/users/user1", "GET", true);
+    urlTests.put("/api/v1/users/user1", "POST", true);
+    urlTests.put("/api/v1/users/user2", "GET", false);
+    urlTests.put("/api/v1/users/user2", "POST", false);
+    urlTests.put("/api/v1/groups", "GET", false);
+    urlTests.put("/api/v1/ldap_sync_events", "GET", false);
+    urlTests.put("/any/other/URL", "GET", true);
+    urlTests.put("/any/other/URL", "POST", false);
+
+    performGeneralDoFilterTest("user1", new int[] {PermissionEntity.CLUSTER_READ_PERMISSION}, urlTests);
+  }
+
+  @Test
+  public void testDoFilter_clusterOperatorAccess() throws Exception {
+    final Table<String, String, Boolean> urlTests = HashBasedTable.create();
+    urlTests.put("/api/v1/clusters/cluster", "GET",  true);
+    urlTests.put("/api/v1/clusters/cluster", "POST",  true);
+    urlTests.put("/api/v1/views", "GET", true);
+    urlTests.put("/api/v1/views", "POST", false);
+    urlTests.put("/api/v1/persist/SomeValue", "GET", true);
+    urlTests.put("/api/v1/persist/SomeValue", "POST", true);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "GET", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "POST", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "GET", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "POST", false);
+    urlTests.put("/api/v1/users/user1", "GET", true);
+    urlTests.put("/api/v1/users/user1", "POST", true);
+    urlTests.put("/api/v1/users/user2", "GET", false);
+    urlTests.put("/api/v1/users/user2", "POST", false);
+    urlTests.put("/api/v1/groups", "GET", false);
+    urlTests.put("/api/v1/ldap_sync_events", "GET", false);
+    urlTests.put("/any/other/URL", "GET", true);
+    urlTests.put("/any/other/URL", "POST", false);
+
+    performGeneralDoFilterTest("user1", new int[] {PermissionEntity.CLUSTER_OPERATE_PERMISSION}, urlTests);
+  }
+
+  @Test
+  public void testDoFilter_viewUserAccess() throws Exception {
+    final Table<String, String, Boolean> urlTests = HashBasedTable.create();
+    urlTests.put("/api/v1/clusters/cluster", "GET",  true);
+    urlTests.put("/api/v1/clusters/cluster", "POST",  false);
+    urlTests.put("/api/v1/views", "GET", true);
+    urlTests.put("/api/v1/views", "POST", true);
+    urlTests.put("/api/v1/persist/SomeValue", "GET", true);
+    urlTests.put("/api/v1/persist/SomeValue", "POST", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "GET", true);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "POST", true);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "GET", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "POST", false);
+    urlTests.put("/api/v1/users/user1", "GET", true);
+    urlTests.put("/api/v1/users/user1", "POST", true);
+    urlTests.put("/api/v1/users/user2", "GET", false);
+    urlTests.put("/api/v1/users/user2", "POST", false);
+    urlTests.put("/api/v1/groups", "GET", false);
+    urlTests.put("/api/v1/ldap_sync_events", "GET", false);
+    urlTests.put("/any/other/URL", "GET", true);
+    urlTests.put("/any/other/URL", "POST", false);
+
+    performGeneralDoFilterTest("user1", new int[] {PermissionEntity.VIEW_USE_PERMISSION}, urlTests);
+  }
+
+  @Test
+  public void testDoFilter_userNoPermissionsAccess() throws Exception {
+    final Table<String, String, Boolean> urlTests = HashBasedTable.create();
+    urlTests.put("/api/v1/clusters/cluster", "GET",  true);
+    urlTests.put("/api/v1/clusters/cluster", "POST",  false);
+    urlTests.put("/api/v1/views", "GET", true);
+    urlTests.put("/api/v1/views", "POST", false);
+    urlTests.put("/api/v1/persist/SomeValue", "GET", true);
+    urlTests.put("/api/v1/persist/SomeValue", "POST", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "GET", false);
+    urlTests.put("/views/AllowedView/SomeVersion/SomeInstance", "POST", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "GET", false);
+    urlTests.put("/views/DeniedView/AnotherVersion/AnotherInstance", "POST", false);
+    urlTests.put("/api/v1/users/user1", "GET", false);
+    urlTests.put("/api/v1/users/user1", "POST", false);
+    urlTests.put("/api/v1/users/user2", "GET", true);
+    urlTests.put("/api/v1/users/user2", "POST", true);
+    urlTests.put("/any/other/URL", "GET", true);
+    urlTests.put("/any/other/URL", "POST", false);
+
+    performGeneralDoFilterTest("user2", new int[0], urlTests);
+  }
+
+  /**
+   * Creates mocks with given permissions and performs all given url tests.
+   *
+   * @param username user name
+   * @param permissionsGranted array of user permissions
+   * @param urlTests map of triples: url - http method - is allowed
+   * @throws Exception
+   */
+  private void performGeneralDoFilterTest(String username, final int[] permissionsGranted, Table<String, String, Boolean> urlTests) throws Exception {
+    final SecurityContext securityContext = createNiceMock(SecurityContext.class);
+    final Authentication authentication = createNiceMock(Authentication.class);
+    final FilterConfig filterConfig = createNiceMock(FilterConfig.class);
+    final AmbariAuthorizationFilter filter = createMockBuilder(AmbariAuthorizationFilter.class)
+        .addMockedMethod("getSecurityContext").addMockedMethod("getViewRegistry").withConstructor().createMock();
+    final List<AmbariGrantedAuthority> authorities = new ArrayList<AmbariGrantedAuthority>();
+    final ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class);
+
+    for (int permissionGranted: permissionsGranted) {
+      final AmbariGrantedAuthority authority = createNiceMock(AmbariGrantedAuthority.class);
+      final PrivilegeEntity privilegeEntity = createNiceMock(PrivilegeEntity.class);
+      final PermissionEntity permission = createNiceMock(PermissionEntity.class);
+
+      expect(authority.getPrivilegeEntity()).andReturn(privilegeEntity).anyTimes();
+      expect(privilegeEntity.getPermission()).andReturn(permission).anyTimes();
+      expect(permission.getId()).andReturn(permissionGranted).anyTimes();
+
+      replay(authority, privilegeEntity, permission);
+      authorities.add(authority);
+    }
+
+    EasyMock.<Collection<? extends GrantedAuthority>>expect(authentication.getAuthorities()).andReturn(authorities).anyTimes();
+    expect(filterConfig.getInitParameter("realm")).andReturn("AuthFilter").anyTimes();
+    expect(authentication.isAuthenticated()).andReturn(true).anyTimes();
+    expect(authentication.getName()).andReturn(username).anyTimes();
+    expect(filter.getSecurityContext()).andReturn(securityContext).anyTimes();
+    expect(filter.getViewRegistry()).andReturn(viewRegistry).anyTimes();
+    expect(securityContext.getAuthentication()).andReturn(authentication).anyTimes();
+    expect(viewRegistry.checkPermission(EasyMock.eq("AllowedView"), EasyMock.<String>anyObject(), EasyMock.<String>anyObject(), EasyMock.anyBoolean())).andAnswer(new IAnswer<Boolean>() {
+      @Override
+      public Boolean answer() throws Throwable {
+        for (int permissionGranted: permissionsGranted) {
+          if (permissionGranted == PermissionEntity.VIEW_USE_PERMISSION) {
+            return true;
+          }
+        }
+        return false;
+      }
+    }).anyTimes();
+    expect(viewRegistry.checkPermission(EasyMock.eq("DeniedView"), EasyMock.<String>anyObject(), EasyMock.<String>anyObject(), EasyMock.anyBoolean())).andReturn(false).anyTimes();
+
+    replay(authentication, filterConfig, filter, securityContext, viewRegistry);
+
+    for (final Cell<String, String, Boolean> urlTest: urlTests.cellSet()) {
+      final FilterChain chain = EasyMock.createStrictMock(FilterChain.class);
+      final HttpServletRequest request = createNiceMock(HttpServletRequest.class);
+      final HttpServletResponse response = createNiceMock(HttpServletResponse.class);
+
+      expect(request.getRequestURI()).andReturn(urlTest.getRowKey()).anyTimes();
+      expect(request.getMethod()).andReturn(urlTest.getColumnKey()).anyTimes();
+      if (urlTest.getValue()) {
+        chain.doFilter(EasyMock.<ServletRequest>anyObject(), EasyMock.<ServletResponse>anyObject());
+        EasyMock.expectLastCall().once();
+      }
+
+      replay(request, response, chain);
+
+      try {
+        filter.doFilter(request, response, chain);
+      } catch (AssertionError error) {
+        throw new Exception("doFilter() should not be chained on " + urlTest.getColumnKey() + " " + urlTest.getRowKey(), error);
+      }
+
+      try {
+        verify(chain);
+      } catch (AssertionError error) {
+        throw new Exception("verify( failed on " + urlTest.getColumnKey() + " " + urlTest.getRowKey(), error);
+      }
+    }
+  }
+
+  @Test
+  public void testParseUserName() throws Exception {
+    final String[] pathesToTest = {
+        "/api/v1/users/user",
+        "/api/v1/users/user?fields=*",
+        "/api/v22/users/user?fields=*"
+    };
+    for (String contextPath: pathesToTest) {
+      final String username = AmbariAuthorizationFilter.parseUserName(contextPath);
+      Assert.assertEquals("user", username);
+    }
+  }
+
+  @Test
+  public void testParseViewContextPath() throws Exception {
+    final String[] pathesToTest = {
+        AmbariAuthorizationFilter.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1",
+        AmbariAuthorizationFilter.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1/index.html",
+        AmbariAuthorizationFilter.VIEWS_CONTEXT_PATH_PREFIX + "MY_VIEW/1.0.0/INSTANCE1/api/test"
+    };
+    for (String contextPath: pathesToTest) {
+      final ViewInstanceVersionDTO dto = AmbariAuthorizationFilter.parseViewInstanceInfo(contextPath);
+      Assert.assertEquals("INSTANCE1", dto.getInstanceName());
+      Assert.assertEquals("MY_VIEW", dto.getViewName());
+      Assert.assertEquals("1.0.0", dto.getVersion());
+    }
+  }
 }

+ 1 - 0
ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java

@@ -313,4 +313,5 @@ public class TestUsers {
 
     userDAO.merge(ldapUser);
   }
+
 }

+ 54 - 9
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java

@@ -18,6 +18,13 @@
 
 package org.apache.ambari.server.upgrade;
 
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.CLUSTER_STATE_STACK_HDP_2_1;
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.JOBS_VIEW_NAME;
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.SHOW_JOBS_FOR_NON_ADMIN_KEY;
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.YARN_RESOURCEMANAGER_WEBAPP_ADDRESS_PROPERTY;
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.YARN_SITE;
+import static org.apache.ambari.server.upgrade.UpgradeCatalog170.YARN_TIMELINE_SERVICE_WEBAPP_ADDRESS_PROPERTY;
+
 import com.google.inject.Binder;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -49,8 +56,10 @@ import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.dao.ViewDAO;
 import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
+import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
+import org.apache.ambari.server.orm.entities.ClusterStateEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntityPK;
@@ -59,6 +68,7 @@ import org.apache.ambari.server.orm.entities.HostComponentStateEntityPK;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostRoleCommandEntity;
 import org.apache.ambari.server.orm.entities.KeyValueEntity;
+import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
@@ -74,7 +84,9 @@ import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.HostComponentAdminState;
 import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.view.ViewRegistry;
 import org.easymock.Capture;
+import org.easymock.IAnswer;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -116,6 +128,7 @@ import static org.easymock.EasyMock.createStrictMock;
 import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.getCurrentArguments;
 import static org.easymock.EasyMock.isA;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.reset;
@@ -422,6 +435,9 @@ public class UpgradeCatalog170Test {
     Clusters clusters = createStrictMock(Clusters.class);
     Config config = createStrictMock(Config.class);
     Config pigConfig = createStrictMock(Config.class);
+    ViewRegistry viewRegistry = createNiceMock(ViewRegistry.class);
+    PermissionEntity adminPermission = createNiceMock(PermissionEntity.class);
+    PermissionEntity viewUsePermission = createNiceMock(PermissionEntity.class);
 
     ClusterConfigEntity clusterConfigEntity = createNiceMock(ClusterConfigEntity.class);
     ConfigGroupConfigMappingDAO configGroupConfigMappingDAO = createNiceMock(ConfigGroupConfigMappingDAO.class);
@@ -448,6 +464,8 @@ public class UpgradeCatalog170Test {
     TypedQuery<HostRoleCommandEntity> q = createNiceMock(TypedQuery.class);
     List<HostRoleCommandEntity> r = new ArrayList<HostRoleCommandEntity>();
     ResultSet userRolesResultSet = createNiceMock(ResultSet.class);
+    ClusterEntity clusterEntity = createNiceMock(ClusterEntity.class);
+    ClusterConfigEntity configEntity = createNiceMock(ClusterConfigEntity.class);
 
     Method m = AbstractUpgradeCatalog.class.getDeclaredMethod
         ("updateConfigurationProperties", String.class, Map.class, boolean.class, boolean.class);
@@ -543,13 +561,20 @@ public class UpgradeCatalog170Test {
     expect(injector.getInstance(PermissionDAO.class)).andReturn(permissionDAO).anyTimes();
     expect(injector.getInstance(PrivilegeDAO.class)).andReturn(privilegeDAO).anyTimes();
     expect(injector.getInstance(KeyValueDAO.class)).andReturn(keyValueDAO).anyTimes();
+    expect(injector.getInstance(ViewRegistry.class)).andReturn(viewRegistry).anyTimes();
 
+    String yarnConfig = String.format("{'%s':'%s', '%s':'%s'}",
+        YARN_TIMELINE_SERVICE_WEBAPP_ADDRESS_PROPERTY, "timeline:8081",
+        YARN_RESOURCEMANAGER_WEBAPP_ADDRESS_PROPERTY, "resource_man:8081");
     expect(configGroupConfigMappingDAO.findAll()).andReturn(configGroupConfigMappingEntities).once();
-    expect(userDAO.findAll()).andReturn(Collections.<UserEntity> emptyList()).times(2);
-    expect(clusterDAO.findAll()).andReturn(Collections.<ClusterEntity> emptyList()).anyTimes();
+    expect(userDAO.findAll()).andReturn(Collections.<UserEntity>emptyList()).times(2);
+    expect(clusterDAO.findAll()).andReturn(Collections.singletonList(clusterEntity)).anyTimes();
+    expect(configEntity.getData()).andReturn(yarnConfig);
+    expect(clusterDAO.findConfig(1L, YARN_SITE, "version1")).andReturn(configEntity).anyTimes();
     expect(viewDAO.findAll()).andReturn(Collections.<ViewEntity> emptyList()).anyTimes();
     expect(viewInstanceDAO.findAll()).andReturn(Collections.<ViewInstanceEntity> emptyList()).anyTimes();
-    expect(permissionDAO.findAmbariAdminPermission()).andReturn(null);
+    expect(permissionDAO.findAmbariAdminPermission()).andReturn(adminPermission).anyTimes();
+    expect(permissionDAO.findViewUsePermission()).andReturn(viewUsePermission).anyTimes();
     expect(permissionDAO.findClusterOperatePermission()).andReturn(null);
     expect(permissionDAO.findClusterReadPermission()).andReturn(null);
 
@@ -559,18 +584,36 @@ public class UpgradeCatalog170Test {
     ViewEntity jobsView = createNiceMock(ViewEntity.class);
     KeyValueEntity showJobsKeyValue = createNiceMock(KeyValueEntity.class);
     UserEntity user = createNiceMock(UserEntity.class);
-
+    ClusterConfigMappingEntity configMappingEntity = createNiceMock(ClusterConfigMappingEntity.class);
+    ClusterStateEntity clusterStateEntity = createNiceMock(ClusterStateEntity.class);
+
+    expect(clusterEntity.getClusterId()).andReturn(1L).anyTimes();
+    expect(clusterEntity.getConfigMappingEntities()).andReturn(Collections.singleton(configMappingEntity));
+    expect(clusterEntity.getClusterStateEntity()).andReturn(clusterStateEntity).anyTimes();
+    expect(clusterStateEntity.getCurrentStackVersion()).andReturn(CLUSTER_STATE_STACK_HDP_2_1);
+    expect(configMappingEntity.getType()).andReturn(YARN_SITE).anyTimes();
+    expect(configMappingEntity.isSelected()).andReturn(1).anyTimes();
+    expect(configMappingEntity.getTag()).andReturn("version1");
     expect(userDAO.findAll()).andReturn(Collections.singletonList(user));
-    expect(jobsView.getCommonName()).andReturn(UpgradeCatalog170.JOBS_VIEW_NAME);
+    expect(jobsView.getCommonName()).andReturn(JOBS_VIEW_NAME);
     expect(jobsView.getVersion()).andReturn("1.0.0");
-    expect(viewDAO.findByCommonName(UpgradeCatalog170.JOBS_VIEW_NAME)).andReturn(jobsView).once();
+    expect(viewDAO.findByCommonName(JOBS_VIEW_NAME)).andReturn(jobsView).once();
     expect(showJobsKeyValue.getValue()).andReturn("true");
-    expect(keyValueDAO.findByKey(UpgradeCatalog170.SHOW_JOBS_FOR_NON_ADMIN_KEY)).andReturn(showJobsKeyValue);
+    expect(keyValueDAO.findByKey(SHOW_JOBS_FOR_NON_ADMIN_KEY)).andReturn(showJobsKeyValue);
     expect(privilegeDAO.findAllByPrincipal(anyObject(List.class))).andReturn(Collections.<PrivilegeEntity>emptyList());
     expect(viewDAO.merge(jobsView)).andReturn(jobsView);
 
     resourceDAO.create(anyObject(ResourceEntity.class));
+    expect(adminPermission.getId()).andReturn(3);
+    expect(viewUsePermission.getId()).andReturn(4);
     viewInstanceDAO.create(anyObject(ViewInstanceEntity.class));
+    expectLastCall().andAnswer(new IAnswer<Object>() {
+      @Override
+      public Object answer() throws Throwable {
+        ((ViewInstanceEntity) getCurrentArguments()[0]).getResource().setId(1L);
+        return null;
+      }
+    });
     keyValueDAO.remove(showJobsKeyValue);
     privilegeDAO.create(anyObject(PrivilegeEntity.class));
 
@@ -579,7 +622,8 @@ public class UpgradeCatalog170Test {
     replay(dbAccessor, configuration, injector, cluster, clusters, amc, config, configHelper, pigConfig);
     replay(userDAO, clusterDAO, viewDAO, viewInstanceDAO, permissionDAO, configGroupConfigMappingDAO);
     replay(resourceTypeDAO, resourceDAO, keyValueDAO, privilegeDAO, clusterConfigEntity);
-    replay(jobsView, showJobsKeyValue, user);
+    replay(jobsView, showJobsKeyValue, user, viewRegistry, viewUsePermission, adminPermission);
+    replay(clusterEntity, configEntity, configMappingEntity, clusterStateEntity);
 
     Class<?> c = AbstractUpgradeCatalog.class;
     Field f = c.getDeclaredField("configuration");
@@ -595,7 +639,8 @@ public class UpgradeCatalog170Test {
     upgradeCatalog.executeDMLUpdates();
 
     verify(upgradeCatalog, dbAccessor, configuration, injector, cluster, clusters, amc, config, configHelper,
-        jobsView, showJobsKeyValue, privilegeDAO, viewDAO, viewInstanceDAO, resourceDAO, keyValueDAO, userRolesResultSet);
+        jobsView, showJobsKeyValue, privilegeDAO, viewDAO, viewInstanceDAO, resourceDAO, keyValueDAO,
+        viewRegistry, userRolesResultSet, clusterEntity, configEntity, configMappingEntity, clusterStateEntity);
   }
 
 

+ 10 - 1
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeTest.java

@@ -28,6 +28,8 @@ import org.apache.ambari.server.controller.ControllerModule;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.*;
 import org.apache.ambari.server.utils.VersionUtils;
+import org.apache.ambari.server.view.ViewRegistry;
+import org.easymock.EasyMock;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -148,7 +150,14 @@ public class UpgradeTest {
   }
 
   private void performUpgrade(String targetVersion) throws Exception {
-    Injector injector = Guice.createInjector(new SchemaUpgradeHelper.UpgradeHelperModule(properties));
+    Injector injector = Guice.createInjector(new SchemaUpgradeHelper.UpgradeHelperModule(properties) {
+      @Override
+      protected void configure() {
+        super.configure();
+        ViewRegistry viewRegistryMock = EasyMock.createNiceMock(ViewRegistry.class);
+        bind(ViewRegistry.class).toInstance(viewRegistryMock);
+      }
+    });
     SchemaUpgradeHelper schemaUpgradeHelper = injector.getInstance(SchemaUpgradeHelper.class);
 
     LOG.info("Upgrading schema to target version = " + targetVersion);

+ 12 - 0
ambari-server/src/test/python/stacks/2.0.6/OOZIE/test_oozie_server.py

@@ -48,6 +48,7 @@ class TestOozieServer(RMFTestCase):
     self.assertResourceCalled('Execute', ' hadoop --config /etc/hadoop/conf dfs -put /usr/lib/oozie/share /user/oozie ; hadoop --config /etc/hadoop/conf dfs -chmod -R 755 /user/oozie/share',
         not_if = " hadoop --config /etc/hadoop/conf dfs -ls /user/oozie/share | awk 'BEGIN {count=0;} /share/ {count++} END {if (count > 0) {exit 0} else {exit 1}}'",
         user = 'oozie',
+        path = ['/usr/bin:/usr/bin'],
         )
     self.assertResourceCalled('Execute', 'cd /var/tmp/oozie && /usr/lib/oozie/bin/oozie-start.sh',
         not_if = 'ls /var/run/oozie/oozie.pid >/dev/null 2>&1 && ps `cat /var/run/oozie/oozie.pid` >/dev/null 2>&1',
@@ -94,6 +95,7 @@ class TestOozieServer(RMFTestCase):
     self.assertResourceCalled('Execute', '/usr/bin/kinit -kt /etc/security/keytabs/oozie.service.keytab oozie/c6402.ambari.apache.org@EXAMPLE.COM; hadoop --config /etc/hadoop/conf dfs -put /usr/lib/oozie/share /user/oozie ; hadoop --config /etc/hadoop/conf dfs -chmod -R 755 /user/oozie/share',
                               not_if = "/usr/bin/kinit -kt /etc/security/keytabs/oozie.service.keytab oozie/c6402.ambari.apache.org@EXAMPLE.COM; hadoop --config /etc/hadoop/conf dfs -ls /user/oozie/share | awk 'BEGIN {count=0;} /share/ {count++} END {if (count > 0) {exit 0} else {exit 1}}'",
                               user = 'oozie',
+                              path = ['/usr/bin:/usr/bin'],
                               )
     self.assertResourceCalled('Execute', 'cd /var/tmp/oozie && /usr/lib/oozie/bin/oozie-start.sh',
                               not_if = 'ls /var/run/oozie/oozie.pid >/dev/null 2>&1 && ps `cat /var/run/oozie/oozie.pid` >/dev/null 2>&1',
@@ -202,6 +204,11 @@ class TestOozieServer(RMFTestCase):
                               recursive = True,
                               mode = 0755,
                               )
+    self.assertResourceCalled('Directory', '/var/lib/oozie/oozie-server/conf',
+                              owner = 'oozie',
+                              recursive = True,
+                              mode = 0755,
+                              )
     self.assertResourceCalled('Execute', 'cd /usr/lib/oozie && tar -xvf oozie-sharelib.tar.gz',
                               not_if = 'ls /var/run/oozie/oozie.pid >/dev/null 2>&1 && ps `cat /var/run/oozie/oozie.pid` >/dev/null 2>&1',
                               )
@@ -307,6 +314,11 @@ class TestOozieServer(RMFTestCase):
                               recursive = True,
                               mode = 0755,
                               )
+    self.assertResourceCalled('Directory', '/var/lib/oozie/oozie-server/conf',
+                              owner = 'oozie',
+                              recursive = True,
+                              mode = 0755,
+                              )
     self.assertResourceCalled('Execute', 'cd /usr/lib/oozie && tar -xvf oozie-sharelib.tar.gz',
                               not_if = 'ls /var/run/oozie/oozie.pid >/dev/null 2>&1 && ps `cat /var/run/oozie/oozie.pid` >/dev/null 2>&1',
                               )

+ 2 - 1
ambari-server/src/test/python/stacks/2.0.6/OOZIE/test_service_check.py

@@ -51,6 +51,7 @@ class TestServiceCheck(RMFTestCase):
     self.assertResourceCalled('Execute', '/tmp/oozieSmoke2.sh',
         logoutput = True,
         tries = 3,
-        command = '/tmp/oozieSmoke2.sh suse /etc/oozie/conf /etc/hadoop/conf ambari-qa False',
+        command = '/tmp/oozieSmoke2.sh suse /etc/oozie/conf /usr/bin /etc/hadoop/conf /usr/bin ambari-qa False',
+        path = ['/usr/bin:/usr/bin'],
         try_sleep = 5,
     )

+ 53 - 4
ambari-web/app/controllers/main/service/manage_config_groups_controller.js

@@ -176,6 +176,7 @@ App.ManageConfigGroupsController = Em.Controller.extend({
       data.items.forEach(function (configGroup) {
         configGroup = configGroup.ConfigGroup;
         var hostNames = configGroup.hosts.mapProperty('host_name');
+        var publicHostNames = this.hostsToPublic(hostNames);
         var newConfigGroup = App.ConfigGroup.create({
           id: configGroup.id,
           name: configGroup.group_name,
@@ -184,6 +185,7 @@ App.ManageConfigGroupsController = Em.Controller.extend({
           parentConfigGroup: defaultConfigGroup,
           service: App.Service.find().findProperty('serviceName', configGroup.tag),
           hosts: hostNames,
+          publicHosts: publicHostNames,
           configSiteTags: [],
           properties: [],
           apiResponse: configGroup
@@ -208,6 +210,7 @@ App.ManageConfigGroupsController = Em.Controller.extend({
       }, this);
       defaultConfigGroup.set('childConfigGroups', configGroups);
       defaultConfigGroup.set('hosts', unusedHosts);
+      defaultConfigGroup.set('publicHosts', this.hostsToPublic(unusedHosts));
       var allGroups = [defaultConfigGroup].concat(configGroups);
       this.set('configGroups', allGroups);
       var originalGroups = this.copyConfigGroups(allGroups);
@@ -216,6 +219,43 @@ App.ManageConfigGroupsController = Em.Controller.extend({
       this.set('isLoaded', true);
     }
   },
+  /**
+   * Get public_host_name by host_name.
+   *
+   * @param {Array|String} hostsList
+   * @return {Array|String}
+   **/
+  hostsToPublic: function(hostsList) {
+    return this.convertHostNames(hostsList, true);
+  },
+  /**
+   * Get host_name by public_host_name
+   *
+   * @param {Array|String} hostsList
+   * @return {Array|String}
+   **/
+  publicToHostName: function(hostsList) {
+    return this.convertHostNames(hostsList, false);
+  },
+  /***
+   * Switch between public_host_name and host_name
+   *
+   * @param {Array|String} hostsList
+   * @param {Boolean} toPublic
+   * @return {Array|String}
+   **/
+  convertHostNames: function(hostsList, toPublic) {
+    var allHosts = this.get('clusterHosts');
+    var convertTarget = !!toPublic ?
+      { from: 'hostName', to: 'publicHostName' } : { from: 'publicHostName', to: 'hostName'};
+    if (this.get('isInstaller')) {
+      allHosts = App.router.get(!!this.get('isAddService') ? 'addServiceController' : 'installerController').get('allHosts');
+    }
+    if (typeof hostsList == 'string') return allHosts.findProperty(convertTarget.from, hostsList).get(convertTarget.to);
+    return hostsList.map(function(hostName) {
+      return allHosts.findProperty(convertTarget.from, hostName).get(convertTarget.to);
+    }, this);
+  },
 
   onLoadConfigGroupsError: function () {
     console.error('Unable to load config groups for service.');
@@ -286,12 +326,16 @@ App.ManageConfigGroupsController = Em.Controller.extend({
     var group = this.get('selectedConfigGroup');
     if (selectedHosts) {
       var defaultHosts = group.get('parentConfigGroup.hosts').slice();
+      var defaultPublicHosts = group.get('parentConfigGroup.publicHosts').slice();
       var configGroupHosts = group.get('hosts');
       selectedHosts.forEach(function (hostName) {
         configGroupHosts.pushObject(hostName);
+        group.get('publicHosts').pushObject(this.hostsToPublic(hostName));
         defaultHosts.removeObject(hostName);
-      });
+        defaultPublicHosts.removeObject(this.hostsToPublic(hostName));
+      }, this);
       group.set('parentConfigGroup.hosts', defaultHosts);
+      group.set('parentConfigGroup.publicHosts', this.hostsToPublic(defaultHosts));
     }
   },
 
@@ -304,11 +348,15 @@ App.ManageConfigGroupsController = Em.Controller.extend({
     }
     var groupHosts = this.get('selectedConfigGroup.hosts');
     var defaultGroupHosts = this.get('selectedConfigGroup.parentConfigGroup.hosts').slice();
+    var defaultGroupPublicHosts = this.get('selectedConfigGroup.parentConfigGroup.publicHosts').slice();
     this.get('selectedHosts').slice().forEach(function (hostName) {
-      defaultGroupHosts.pushObject(hostName);
-      groupHosts.removeObject(hostName);
-    });
+      defaultGroupHosts.pushObject(this.publicToHostName(hostName));
+      defaultGroupPublicHosts.pushObject(hostName);
+      groupHosts.removeObject(this.publicToHostName(hostName));
+      this.get('selectedConfigGroup.publicHosts').removeObject(hostName);
+    }, this);
     this.set('selectedConfigGroup.parentConfigGroup.hosts', defaultGroupHosts);
+    this.set('selectedConfigGroup.parentConfigGroup.publicHosts', this.hostsToPublic(defaultGroupHosts));
     this.set('selectedHosts', []);
   },
 
@@ -450,6 +498,7 @@ App.ManageConfigGroupsController = Em.Controller.extend({
           parentConfigGroup: defaultConfigGroup,
           service: Em.Object.create({id: self.get('serviceName')}),
           hosts: [],
+          publicHosts: [],
           configSiteTags: [],
           properties: []
         });

+ 1 - 0
ambari-web/app/controllers/wizard.js

@@ -909,6 +909,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, {
           name: configGroup.get('name'),
           description: configGroup.get('description'),
           hosts: hostNames,
+          publicHosts: configGroup.get('hosts').map(function(hostName) {return App.router.get('manageConfigGroupsController').hostsToPublic(hostName); }),
           properties: properties,
           isDefault: configGroup.get('isDefault'),
           isForInstalledService: isForInstalledService,

+ 12 - 2
ambari-web/app/controllers/wizard/step7_controller.js

@@ -318,8 +318,8 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
       service = this.get('stepConfigs').findProperty('serviceName', serviceName),
       defaultConfigGroupHosts = this.get('wizardController.allHosts').mapProperty('hostName'),
       siteToTagMap = this._createSiteToTagMap(data.Clusters.desired_configs, params.serviceConfigsDef.get('configTypes')),
-      selectedConfigGroup;
-
+      selectedConfigGroup,
+      manageCGController = App.router.get('manageConfigGroupsController');
     this.set('loadedClusterSiteToTagMap', siteToTagMap);
 
     //parse loaded config groups
@@ -338,6 +338,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
               parentConfigGroup: null,
               service: App.Service.find().findProperty('serviceName', item.tag),
               hosts: groupHosts,
+              publicHosts: manageCGController.hostsToPublic(groupHosts),
               configSiteTags: []
             });
             groupHosts.forEach(function (host) {
@@ -359,6 +360,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
       description: "Default cluster level " + serviceName + " configuration",
       isDefault: true,
       hosts: defaultConfigGroupHosts,
+      publicHosts: manageCGController.hostsToPublic(defaultConfigGroupHosts),
       parentConfigGroup: null,
       service: Em.Object.create({
         id: serviceName
@@ -1013,6 +1015,8 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
   loadConfigGroups: function (serviceConfigGroups) {
     var services = this.get('stepConfigs');
     var hosts = this.get('wizardController.allHosts').mapProperty('hostName');
+    var manageCGController = App.router.get('manageConfigGroupsController');
+    this.setupManageConfigGroupsController();
     services.forEach(function (service) {
       if (service.get('serviceName') === 'MISC') return;
       var serviceRawGroups = serviceConfigGroups.filterProperty('service.id', service.serviceName);
@@ -1023,6 +1027,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
             description: "Default cluster level " + service.serviceName + " configuration",
             isDefault: true,
             hosts: Em.copy(hosts),
+            publicHosts: Em.copy(manageCGController.hostsToPublic(hosts)),
             service: Em.Object.create({
               id: service.serviceName
             }),
@@ -1056,6 +1061,11 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
     });
   },
 
+  setupManageConfigGroupsController: function() {
+    var manageCGController = App.router.get('manageConfigGroupsController');
+    manageCGController.set('isInstaller', true);
+    manageCGController.set('isAddService', this.get('wizardController.name') === 'addServiceController');
+  },
   /**
    * Click-handler on config-group to make it selected
    * @param {object} event

+ 5 - 0
ambari-web/app/models/config_group.js

@@ -81,6 +81,11 @@ App.ConfigGroup = Ember.Object.extend({
    */
   hosts: [],
 
+  /**
+   * Public host names related by host_name.
+   */
+  publicHosts: [],
+
   /**
    * this flag is used for installed services' config groups
    * if user make changes to them - mark this flag to true

+ 1 - 1
ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs

@@ -54,7 +54,7 @@
                     <div class="row-fluid">
                         <div class="span12 pull-right">
                           {{view Em.Select
-                          contentBinding="selectedConfigGroup.hosts"
+                          contentBinding="selectedConfigGroup.publicHosts"
                           multiple="multiple"
                           class="group-select"
                           selectionBinding="selectedHosts"

+ 2 - 2
contrib/views/jobs/src/main/resources/view.xml

@@ -21,11 +21,11 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
   <parameter>
       <name>yarn.ats.url</name>
       <description>The URL to the YARN Application Timeline Server, used to provide Jobs information, typically, this is the yarn.timeline-service.webapp.address property in the yarn-site.xml configuration. For example: http://yarn.ats.address:8188</description>
-      <required>false</required>
+      <required>true</required>
   </parameter>
   <parameter>
       <name>yarn.resourcemanager.url</name>
       <description>The URL to the YARN ResourceManager, used to provide YARN Application data. For example: http://yarn.resourcemanager.address:8088</description>
-      <required>false</required>
+      <required>true</required>
   </parameter>
 </view>

+ 2 - 2
contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java

@@ -590,10 +590,10 @@ public class SliderAppsViewControllerImpl implements SliderAppsViewController {
             if (metainfo.getApplication() != null) {
               Application application = metainfo.getApplication();
               String appConfigJsonString = IOUtils.toString(
-                  zipFile.getInputStream(zipFile.getEntry("appConfig.json")),
+                  zipFile.getInputStream(zipFile.getEntry("appConfig-default.json")),
                   "UTF-8");
               String resourcesJsonString = IOUtils.toString(
-                  zipFile.getInputStream(zipFile.getEntry("resources.json")),
+                  zipFile.getInputStream(zipFile.getEntry("resources-default.json")),
                   "UTF-8");
               JsonElement appConfigJson = new JsonParser()
                   .parse(appConfigJsonString);

+ 2 - 1
contrib/views/slider/src/main/resources/ui/app/controllers/slider_apps_controller.js

@@ -20,7 +20,8 @@ App.SliderAppsController = Ember.ArrayController.extend({
   /**
    * show modal popup that says apps currently unavailable
    */
-  showUnavailableAppsPopup: function() {
+  showUnavailableAppsPopup: function(message) {
+    this.set('errorMessage', message);
     Bootstrap.ModalManager.open(
       "apps-warning-modal",
       Em.I18n.t('common.warning'),

+ 5 - 6
contrib/views/slider/src/main/resources/ui/app/controllers/slider_controller.js

@@ -88,15 +88,14 @@ App.SliderController = Ember.Controller.extend({
     var gangliaCustomClusters = [];
     //parse CSV string with cluster names and ports
     if (!Em.isNone(prop)) {
-      prop.replace(/\'/g, "").split(',').forEach(function(item, index){
-        if (index % 2 === 0) {
+      prop.replace(/\'/g, "").split(',').forEach(function(item){
+        var splits = item.split(':');
+        if (splits.length > 1) {
           gangliaCustomClusters.push({
-            name: item
+            name: splits[0],
+            port: splits[1]
           })
         }
-        else {
-          gangliaCustomClusters[gangliaCustomClusters.length - 1].port = parseInt(item);
-        }
       });
     }
     return gangliaCustomClusters;

+ 4 - 3
contrib/views/slider/src/main/resources/ui/app/mappers/slider_apps_mapper.js

@@ -53,13 +53,14 @@ App.SliderAppsMapper = App.Mapper.createWithMixins(App.RunPeriodically, {
       sender: this,
       success: 'parse'
     }).fail(function(jqXHR, textStatus){
-        if (textStatus === "timeout" && !self.get('isWarningPopupShown')) {
+        if (!self.get('isWarningPopupShown')) {
+          var message = textStatus === "timeout" ? "timeout" : jqXHR.responseText;
           self.set('isWarningPopupShown', true);
-          window.App.__container__.lookup('controller:SliderApps').showUnavailableAppsPopup();
+          window.App.__container__.lookup('controller:SliderApps').showUnavailableAppsPopup(message);
         }
       }).complete(function(){
         dfd.resolve();
-      })
+      });
     return dfd.promise();
   },
 

+ 3 - 0
contrib/views/slider/src/main/resources/ui/app/templates/unavailable_apps.hbs

@@ -17,3 +17,6 @@
 }}
 
 {{t slider.apps.unavailable}}
+<div class="alert alert-danger">
+    {{controller.errorMessage}}
+</div>

+ 1 - 1
contrib/views/slider/src/main/resources/ui/app/translations.js

@@ -81,7 +81,7 @@ Em.I18n.translations = {
 
   'slider.apps.title': 'Slider Apps',
   'slider.apps.create': 'Create App',
-  'slider.apps.unavailable': 'HDFS or YARN services are currently unaccessible',
+  'slider.apps.unavailable': 'Unable to get list of Slider Apps due to issues below. Possible reasons include incorrect or invalid view parameters. Please contact administrator for setting up proper view parameters and verifying necessary services are working.',
   'sliderApps.filters.info': '{0} of {1} sliders showing',
 
   'sliderApp.flex.invalid_counts': 'Instance counts should be integer and >= 0',

+ 1 - 1
contrib/views/slider/src/main/resources/view.xml

@@ -50,7 +50,7 @@ limitations under the License. Kerberos, LDAP, Custom. Binary/Htt
   </parameter>
   <parameter>
     <name>ganglia.custom.clusters</name>
-    <description>Custom Ganglia clusters which can be used by applications created in Slider Apps view. Value is comma separated list of cluster-name and port pairs. Typically this is the ganglia_custom_clusters in the ganglia-env configuration. For example: Application1,8881,Application2,8882</description>
+    <description>Custom Ganglia clusters which can be used by applications created in Slider Apps view. Value is comma separated list of cluster-name and port pairs. Typically this is the additional_clusters in the ganglia-env configuration. For example: Application1:8881,Application2:8882</description>
     <required>false</required>
   </parameter>
   <parameter>

+ 14 - 2
docs/pom.xml

@@ -298,6 +298,18 @@
                 WANdisco                
             </organization>            
         </developer>
+        <developer>
+            <id>jaoki</id>
+            <name>Jun Aoki</name>
+            <email>jaoki@apache.org</email>
+            <timezone>-8</timezone>
+            <roles>
+                <role>Committer</role>
+            </roles>
+            <organization>
+                Pivotal
+            </organization>
+        </developer>
         <developer>
             <id>jaimin</id>
             <name>Jaimin Jetly</name>
@@ -621,8 +633,8 @@
            <organization>Teradata</organization>
          </contributor>
          <contributor>
-           <name>Jun Aoki</name>
-           <organization>Pivotal</organization>
+           <name>Krisztian Horvath</name>
+           <organization>SequenceIQ</organization>
          </contributor>
        </contributors>
 

+ 1 - 0
pom.xml

@@ -31,6 +31,7 @@
     <deb.section>universe/admin</deb.section>
     <deb.architecture>i386 amd64</deb.architecture>
     <deb.priority>extra</deb.priority>
+    <stack.distribution>BIGTOP</stack.distribution>
   </properties>
   <pluginRepositories>
     <pluginRepository>