Repository: ambari Updated Branches: refs/heads/branch-feature-AMBARI-14714 c34c86935 -> 332053bcd
AMBARI-21558: Software Registry Recommendations API - Initial patch (jluniya) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/332053bc Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/332053bc Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/332053bc Branch: refs/heads/branch-feature-AMBARI-14714 Commit: 332053bcddca0f1f36249d5f43ee64a1716fe58d Parents: c34c869 Author: Jayush Luniya <[email protected]> Authored: Mon Jul 24 16:51:59 2017 -0700 Committer: Jayush Luniya <[email protected]> Committed: Mon Jul 24 16:51:59 2017 -0700 ---------------------------------------------------------------------- ...egistryRecommendationResourceDefinition.java | 48 +++ .../RegistryValidationResourceDefinition.java | 60 ++++ .../resources/ResourceInstanceFactoryImpl.java | 8 + .../registry/RegistryRecommendationService.java | 74 ++++ .../api/services/registry/RegistryService.java | 2 +- .../registry/RegistryValidationService.java | 74 ++++ .../ambari/server/controller/AmbariServer.java | 3 + .../AbstractControllerResourceProvider.java | 4 + .../internal/DefaultProviderModule.java | 10 + .../RegistryAdvisorResourceProvider.java | 134 ++++++++ .../RegistryRecommendationResourceProvider.java | 125 +++++++ .../RegistryValidationResourceProvider.java | 122 +++++++ .../ambari/server/controller/spi/Resource.java | 4 + .../exceptions/RegistryAdvisorException.java | 41 +++ .../ambari/server/registry/MpackEntry.java | 71 ++++ .../ambari/server/registry/RegistryAdvisor.java | 341 +++++++++++++++++++ .../server/registry/RegistryAdvisorRequest.java | 131 +++++++ .../registry/RegistryAdvisorResponse.java | 121 +++++++ .../RegistryRecommendationResponse.java | 115 +++++++ .../registry/RegistryValidationResponse.java | 124 +++++++ .../apache/ambari/server/utils/SetUtils.java | 40 +++ 21 files changed, 1651 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryRecommendationResourceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryRecommendationResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryRecommendationResourceDefinition.java new file mode 100644 index 0000000..69e6b69 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryRecommendationResourceDefinition.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.api.resources; + +import org.apache.ambari.server.controller.spi.Resource; + +/** + * Registry recommendation resource definition. + */ +public class RegistryRecommendationResourceDefinition extends BaseResourceDefinition { + /** + * Constructor. + */ + public RegistryRecommendationResourceDefinition() { + super(Resource.Type.RegistryRecommendation); + } + + @Override + public String getPluralName() { + return "recommendations"; + } + + @Override + public String getSingularName() { + return "recommendation"; + } + + @Override + public boolean isCreatable() { + return false; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryValidationResourceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryValidationResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryValidationResourceDefinition.java new file mode 100644 index 0000000..af4dcde --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RegistryValidationResourceDefinition.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.api.resources; + +import org.apache.ambari.server.controller.spi.Resource; + +/** + * Registry validation resource definition. + */ +public class RegistryValidationResourceDefinition extends BaseResourceDefinition { + /** + * Constructor. + */ + public RegistryValidationResourceDefinition() { + super(Resource.Type.RegistryValidation); + } + + /** + * {@inheritDoc} + * @return + */ + @Override + public String getPluralName() { + return "validations"; + } + + /** + * {@inheritDoc} + * @return + */ + @Override + public String getSingularName() { + return "validation"; + } + + /** + * {@inheritDoc} + * @return + */ + @Override + public boolean isCreatable() { + return false; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java index 34070e1..73963df 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java @@ -153,6 +153,14 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory { resourceDefinition = new RegistryResourceDefinition(); break; + case RegistryRecommendation: + resourceDefinition = new RegistryRecommendationResourceDefinition(); + break; + + case RegistryValidation: + resourceDefinition = new RegistryValidationResourceDefinition(); + break; + case RegistryScenario: resourceDefinition = new RegistryScenarioResourceDefinition(); break; http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryRecommendationService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryRecommendationService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryRecommendationService.java new file mode 100644 index 0000000..8a01dae --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryRecommendationService.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.api.services.registry; + +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.ambari.annotations.ApiIgnore; +import org.apache.ambari.server.api.resources.ResourceInstance; +import org.apache.ambari.server.api.services.BaseService; +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.controller.spi.Resource; + + +/** + * REST API endpoint for registry recommendations + */ +@Path("/registries/{registryId}/recommendations") +public class RegistryRecommendationService extends BaseService { + + /** + * Create registry recommendation + * @param body Request body + * @param headers Http headers + * @param ui Uri info + * @param registryId Registry id + * @return {@link Response} containing registry recommendation response + */ + @POST + @Produces(MediaType.TEXT_PLAIN) + @ApiIgnore // until documented + public Response createRegistryRecommendation(String body, @Context HttpHeaders headers, @Context UriInfo ui, + @PathParam("registryId") String registryId) { + return handleRequest(headers, body, ui, Request.Type.POST, + createRegistryRecommendationResource(registryId)); + } + + /** + * Create registry recommendation resource instance + * @param registryId Registry id + * @return Registry recommendation resource instance + */ + ResourceInstance createRegistryRecommendationResource(String registryId) { + Map<Resource.Type, String> mapIds = new HashMap<>(); + mapIds.put(Resource.Type.Registry, registryId); + return createResource(Resource.Type.RegistryRecommendation, mapIds); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryService.java index 7704149..05ac1b9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryService.java @@ -64,9 +64,9 @@ public class RegistryService extends BaseService { /** * Handles: POST /registries/ * + * @param body request body * @param headers http headers * @param ui uri info - * @param body request body * @return information regarding the software registry created */ @POST http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryValidationService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryValidationService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryValidationService.java new file mode 100644 index 0000000..20e4b4f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/registry/RegistryValidationService.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.api.services.registry; + +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.ambari.annotations.ApiIgnore; +import org.apache.ambari.server.api.resources.ResourceInstance; +import org.apache.ambari.server.api.services.BaseService; +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.controller.spi.Resource; + + +/** + * REST API endpoint for registry validations + */ +@Path("/registries/{registryId}/validations") +public class RegistryValidationService extends BaseService { + + /** + * Create registry validation + * @param body Request body + * @param headers Http headers + * @param ui Uri info + * @param registryId Registry id + * @return {@link Response} containing registry validation response + */ + @POST + @Produces(MediaType.TEXT_PLAIN) + @ApiIgnore // until documented + public Response createRegistryValidation(String body, @Context HttpHeaders headers, @Context UriInfo ui, + @PathParam("registryId") String registryId) { + return handleRequest(headers, body, ui, Request.Type.POST, + createRegistryValidationResource(registryId)); + } + + /** + * Create registry validation resource + * @param registryId Registry id + * @return Registry validation resource instance + */ + ResourceInstance createRegistryValidationResource(String registryId) { + Map<Resource.Type, String> mapIds = new HashMap<>(); + mapIds.put(Resource.Type.Registry, registryId); + return createResource(Resource.Type.RegistryValidation, mapIds); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index 8988be0..6d4d1c9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java @@ -69,6 +69,7 @@ import org.apache.ambari.server.controller.internal.ClusterResourceProvider; import org.apache.ambari.server.controller.internal.HostResourceProvider; import org.apache.ambari.server.controller.internal.PermissionResourceProvider; import org.apache.ambari.server.controller.internal.PrivilegeResourceProvider; +import org.apache.ambari.server.controller.internal.RegistryAdvisorResourceProvider; import org.apache.ambari.server.controller.internal.StackAdvisorResourceProvider; import org.apache.ambari.server.controller.internal.StackDefinedPropertyProvider; import org.apache.ambari.server.controller.internal.StackDependencyResourceProvider; @@ -91,6 +92,7 @@ import org.apache.ambari.server.orm.dao.ResourceDAO; import org.apache.ambari.server.orm.dao.UserDAO; import org.apache.ambari.server.orm.dao.ViewInstanceDAO; import org.apache.ambari.server.orm.entities.MetainfoEntity; +import org.apache.ambari.server.registry.RegistryAdvisor; import org.apache.ambari.server.resources.ResourceManager; import org.apache.ambari.server.resources.api.rest.GetResource; import org.apache.ambari.server.scheduler.ExecutionScheduleManager; @@ -908,6 +910,7 @@ public class AmbariServer { KeyService.init(injector.getInstance(PersistKeyValueImpl.class)); BootStrapResource.init(injector.getInstance(BootStrapImpl.class)); StackAdvisorResourceProvider.init(injector.getInstance(StackAdvisorHelper.class)); + RegistryAdvisorResourceProvider.init(injector.getInstance(RegistryAdvisor.class)); StageUtils.setGson(injector.getInstance(Gson.class)); StageUtils.setTopologyManager(injector.getInstance(TopologyManager.class)); StageUtils.setConfiguration(injector.getInstance(Configuration.class)); http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java index 5cfcdc7..3228a7f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java @@ -182,6 +182,10 @@ public abstract class AbstractControllerResourceProvider extends AbstractAuthori return new StackResourceProvider(propertyIds, keyPropertyIds, managementController); case Registry: return new RegistryResourceProvider(managementController); + case RegistryRecommendation: + return new RegistryRecommendationResourceProvider(managementController); + case RegistryValidation: + return new RegistryValidationResourceProvider(managementController); case RegistryScenario: return new RegistryScenarioResourceProvider(managementController); case RegistryMpack: http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java index 7466bb4..248abad 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java @@ -92,6 +92,16 @@ public class DefaultProviderModule extends AbstractProviderModule { return new AlertResourceProvider(managementController); case Registry: return new RegistryResourceProvider(managementController); + case RegistryRecommendation: + return new RegistryRecommendationResourceProvider(managementController); + case RegistryValidation: + return new RegistryValidationResourceProvider(managementController); + case RegistryScenario: + return new RegistryScenarioResourceProvider(managementController); + case RegistryMpack: + return new RegistryMpackResourceProvider(managementController); + case RegistryMpackVersion: + return new RegistryMpackVersionResourceProvider(managementController); case Mpack: return new MpackResourceProvider(managementController); case AlertDefinition: http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryAdvisorResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryAdvisorResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryAdvisorResourceProvider.java new file mode 100644 index 0000000..87d8102 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryAdvisorResourceProvider.java @@ -0,0 +1,134 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.registry.MpackEntry; +import org.apache.ambari.server.registry.RegistryAdvisor; +import org.apache.ambari.server.registry.RegistryAdvisorRequest; +import org.apache.ambari.server.registry.RegistryAdvisorRequest.RegistryAdvisorRequestBuilder; +import org.apache.ambari.server.registry.RegistryAdvisorRequest.RegistryAdvisorRequestType; + +import com.google.inject.Inject; + +/** + * Abstract superclass for registry recommendations and validations. + */ +public abstract class RegistryAdvisorResourceProvider extends AbstractControllerResourceProvider { + + public static final String REGISTRY_ID = PropertyHelper.getPropertyId("RegistryInfo", "registry_id"); + protected static final String SELECTED_SCENARIOS_PROPERTY_ID = "selected_scenarios"; + protected static final String SELECTED_MPACKS_PROPERTY_ID = "selected_mpacks"; + + protected static RegistryAdvisor registryAdvisor; + + @Inject + public static void init(RegistryAdvisor instance) { + registryAdvisor = instance; + } + + /** + * Constructor + * @param propertyIds Property ids + * @param keyPropertyIds Key property ids + * @param managementController Ambari management controller instance + */ + protected RegistryAdvisorResourceProvider(Set<String> propertyIds, Map<Resource.Type, String> keyPropertyIds, + AmbariManagementController managementController) { + super(propertyIds, keyPropertyIds, managementController); + } + + /** + * Get property containing {@link RegistryAdvisorRequestType} + * @return Property id + */ + protected abstract String getRequestTypePropertyId(); + + /** + * Create registry advisor request instance + * @param request {@link Request} input + * @return {@link RegistryAdvisorRequest} instance + */ + protected RegistryAdvisorRequest createRegistryAdvisorRequest(Request request) { + try { + Long registryId = Long.valueOf((String) getRequestProperty(request, REGISTRY_ID)); + RegistryAdvisorRequestType requestType = RegistryAdvisorRequestType.fromString( + (String) getRequestProperty(request, getRequestTypePropertyId())); + List<String> selectedScenarios = (List<String>) getRequestProperty(request, SELECTED_SCENARIOS_PROPERTY_ID); + List<MpackEntry> selectedMpacks = parseSelectedMpacksProperty(request); + return RegistryAdvisorRequestBuilder.forRegistry(registryId) + .ofType(requestType) + .forScenarios(selectedScenarios) + .forMpacks(selectedMpacks) + .build(); + } catch (Exception e) { + LOG.warn("Error occurred during preparation of registry advisor request", e); + Response response = Response.status(Response.Status.BAD_REQUEST) + .entity(String.format("Request body is not correct, error: %s", e.getMessage())).build(); + // TODO: Hosts and services must not be empty + throw new WebApplicationException(response); + } + } + + /** + * Parse selected mpacks property from the reuqest + * @param request {@link Request} input + * @return List of selected mpacks + */ + private List<MpackEntry> parseSelectedMpacksProperty(Request request) { + List<MpackEntry> selectedMpacks = new LinkedList<>();request.getProperties(); + Set<Map<String, String>> selectedMpacksProperties = + (Set<Map<String, String>>) getRequestProperty(request, SELECTED_MPACKS_PROPERTY_ID); + if(selectedMpacksProperties != null) { + for (Map<String, String> properties : selectedMpacksProperties) { + String mpackName = properties.get("mpack_name"); + String mpackVersion = properties.get("mpack_version"); + MpackEntry mpackEntry = new MpackEntry(mpackName, mpackVersion); + selectedMpacks.add(mpackEntry); + } + } + return selectedMpacks; + } + + /** + * Get value of property in the request + * @param request {@link Request} input + * @param propertyName Property name + * @return Property value + */ + protected Object getRequestProperty(Request request, String propertyName) { + for (Map<String, Object> propertyMap : request.getProperties()) { + if (propertyMap.containsKey(propertyName)) { + return propertyMap.get(propertyName); + } + } + return null; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryRecommendationResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryRecommendationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryRecommendationResourceProvider.java new file mode 100644 index 0000000..e2b8ea6 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryRecommendationResourceProvider.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.RequestStatus; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException; +import org.apache.ambari.server.controller.spi.SystemException; +import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.registry.RegistryAdvisorRequest; +import org.apache.ambari.server.registry.RegistryRecommendationResponse; + + +/** + * Registry recommendation resource provider + */ +public class RegistryRecommendationResourceProvider extends RegistryAdvisorResourceProvider { + + protected static final String RECOMMEND_PROPERTY_ID = "recommend"; + protected static final String RECOMMENDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId( + "RegistryRecommendation", "id"); + protected static final String RECOMMEND_MPACK_BUNDLES_PROPERTY_ID = PropertyHelper + .getPropertyId("recommendations", "mpack_bundles"); + + private static Set<String> pkPropertyIds = new HashSet<>( + Arrays.asList(REGISTRY_ID, RECOMMENDATION_ID_PROPERTY_ID)); + + /** + * The property ids for a software registry resource. + */ + private static final Set<String> PROPERTY_IDS = new HashSet<>(); + + /** + * The key property ids for a software registry resource. + */ + private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<>(); + + static { + // properties + PROPERTY_IDS.add(REGISTRY_ID); + PROPERTY_IDS.add(RECOMMENDATION_ID_PROPERTY_ID); + PROPERTY_IDS.add(RECOMMEND_PROPERTY_ID); + PROPERTY_IDS.add(SELECTED_SCENARIOS_PROPERTY_ID); + PROPERTY_IDS.add(RECOMMEND_MPACK_BUNDLES_PROPERTY_ID); + + // keys + KEY_PROPERTY_IDS.put(Resource.Type.Registry, REGISTRY_ID); + KEY_PROPERTY_IDS.put(Resource.Type.RegistryRecommendation, RECOMMENDATION_ID_PROPERTY_ID); + + } + + /** + * Constructor + * @param managementController Ambari management controller instance + */ + protected RegistryRecommendationResourceProvider( + final AmbariManagementController managementController) { + super(PROPERTY_IDS, KEY_PROPERTY_IDS, managementController); + } + + /** + * {@inheritDoc} + */ + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } + + /** + * {@inheritDoc} + */ + @Override + public RequestStatus createResourcesAuthorized(Request request) + throws NoSuchParentResourceException, ResourceAlreadyExistsException, SystemException { + RegistryAdvisorRequest registryAdvisorRequest = createRegistryAdvisorRequest(request); + RegistryRecommendationResponse response = createResources(new Command<RegistryRecommendationResponse>() { + @Override + public RegistryRecommendationResponse invoke() throws AmbariException { + return registryAdvisor.recommend(registryAdvisorRequest); + } + }); + Resource resource = new ResourceImpl(Resource.Type.RegistryRecommendation); + resource.setProperty(REGISTRY_ID, response.getRegistryId()); + resource.setProperty(RECOMMENDATION_ID_PROPERTY_ID, response.getId()); + resource.setProperty(RECOMMEND_PROPERTY_ID, response.getRequestType().toString()); + resource.setProperty(SELECTED_SCENARIOS_PROPERTY_ID, response.getSelectedScenarios()); + resource.setProperty(RECOMMEND_MPACK_BUNDLES_PROPERTY_ID, response.getRecommendations().getMpackBundles()); + + Set<Resource> associatedResources = new HashSet<>(Arrays.asList(resource)); + return getRequestStatus(null, associatedResources); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getRequestTypePropertyId() { + return RECOMMEND_PROPERTY_ID; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryValidationResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryValidationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryValidationResourceProvider.java new file mode 100644 index 0000000..bedbb04 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RegistryValidationResourceProvider.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.RequestStatus; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException; +import org.apache.ambari.server.controller.spi.SystemException; +import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.registry.RegistryAdvisorRequest; +import org.apache.ambari.server.registry.RegistryValidationResponse; + + +/** + * Registry validation resource provider + */ +public class RegistryValidationResourceProvider extends RegistryAdvisorResourceProvider { + + protected static final String VALIDATE_PROPERTY_ID = "validate"; + protected static final String VALIDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId( + "RegistryValidation", "id"); + protected static final String ITEMS_PROPERTY_ID = "items"; + + private static Set<String> pkPropertyIds = new HashSet<>( + Arrays.asList(REGISTRY_ID, VALIDATION_ID_PROPERTY_ID)); + + /** + * The property ids for a software registry resource. + */ + private static final Set<String> PROPERTY_IDS = new HashSet<>(); + + /** + * The key property ids for a software registry resource. + */ + private static final Map<Resource.Type, String> KEY_PROPERTY_IDS = new HashMap<>(); + + static { + // properties + PROPERTY_IDS.add(REGISTRY_ID); + PROPERTY_IDS.add(VALIDATION_ID_PROPERTY_ID); + PROPERTY_IDS.add(VALIDATE_PROPERTY_ID); + PROPERTY_IDS.add(SELECTED_SCENARIOS_PROPERTY_ID); + PROPERTY_IDS.add(SELECTED_MPACKS_PROPERTY_ID); + PROPERTY_IDS.add(ITEMS_PROPERTY_ID); + + // keys + KEY_PROPERTY_IDS.put(Resource.Type.Registry, REGISTRY_ID); + KEY_PROPERTY_IDS.put(Resource.Type.RegistryValidation, VALIDATION_ID_PROPERTY_ID); + + } + + protected RegistryValidationResourceProvider( + final AmbariManagementController managementController) { + super(PROPERTY_IDS, KEY_PROPERTY_IDS, managementController); + } + + /** + * {@inheritDoc} + */ + @Override + protected Set<String> getPKPropertyIds() { + return pkPropertyIds; + } + + /** + * {@inheritDoc} + */ + @Override + public RequestStatus createResourcesAuthorized(Request request) + throws NoSuchParentResourceException, ResourceAlreadyExistsException, SystemException { + RegistryAdvisorRequest registryAdvisorRequest = createRegistryAdvisorRequest(request); + RegistryValidationResponse response = createResources(new Command<RegistryValidationResponse>() { + @Override + public RegistryValidationResponse invoke() throws AmbariException { + return registryAdvisor.validate(registryAdvisorRequest); + } + }); + Resource resource = new ResourceImpl(Resource.Type.RegistryValidation); + setResourceProperty(resource, REGISTRY_ID, response.getRegistryId(), getPropertyIds()); + setResourceProperty(resource, VALIDATION_ID_PROPERTY_ID, response.getId(), getPropertyIds()); + setResourceProperty(resource, VALIDATE_PROPERTY_ID, response.getRequestType().toString(), getPropertyIds()); + setResourceProperty(resource, SELECTED_SCENARIOS_PROPERTY_ID, response.getSelectedScenarios(), getPropertyIds()); + setResourceProperty(resource, SELECTED_MPACKS_PROPERTY_ID, response.getSelectedMpacks(), getPropertyIds()); + setResourceProperty(resource, ITEMS_PROPERTY_ID, response.getItems(), getPropertyIds()); + + Set<Resource> associatedResources = new HashSet<>(Arrays.asList(resource)); + return getRequestStatus(null, associatedResources); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getRequestTypePropertyId() { + return VALIDATE_PROPERTY_ID; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index f07ca79..1994501 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -91,6 +91,8 @@ public interface Resource { Group, Member, Registry, + RegistryRecommendation, + RegistryValidation, RegistryScenario, RegistryMpack, RegistryMpackVersion, @@ -217,6 +219,8 @@ public interface Resource { public static final Type Group = InternalType.Group.getType(); public static final Type Member = InternalType.Member.getType(); public static final Type Registry = InternalType.Registry.getType(); + public static final Type RegistryRecommendation = InternalType.RegistryRecommendation.getType(); + public static final Type RegistryValidation = InternalType.RegistryValidation.getType(); public static final Type RegistryScenario = InternalType.RegistryScenario.getType(); public static final Type RegistryMpack = InternalType.RegistryMpack.getType(); public static final Type RegistryMpackVersion = InternalType.RegistryMpackVersion.getType(); http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/exceptions/RegistryAdvisorException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/exceptions/RegistryAdvisorException.java b/ambari-server/src/main/java/org/apache/ambari/server/exceptions/RegistryAdvisorException.java new file mode 100644 index 0000000..c90300f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/exceptions/RegistryAdvisorException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.exceptions; + +/** + * Registry advisor exception + */ +public class RegistryAdvisorException extends Exception { + + /** + * Constructor + * @param message Exception message + */ + public RegistryAdvisorException(String message) { + super(message); + } + + /** + * Constructor + * @param message Exception message + * @param cause Exception cause + */ + public RegistryAdvisorException(String message, Throwable cause) { + super(message, cause); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/MpackEntry.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/MpackEntry.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/MpackEntry.java new file mode 100644 index 0000000..c1029af --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/MpackEntry.java @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import org.codehaus.jackson.annotate.JsonIgnore; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Mpack version recommenation + */ +public class MpackEntry { + private String mpackName; + private String mpackVersion; + private RegistryMpackVersion registryMpackVersion; + + public MpackEntry(String mpackName, String mpackVersion) { + this.mpackName = mpackName; + this.mpackVersion = mpackVersion; + this.registryMpackVersion = registryMpackVersion; + } + + /** + * Get mpack name + * @return + */ + @JsonProperty("mpack_name") + public String getMpackName() { + return mpackName; + } + + /** + * Get version + * @return + */ + @JsonProperty("mpack_version") + public String getMpackVersion() { + return mpackVersion; + } + + /** + * Set registry mpack version + * @param registryMpackVersion + */ + public void setRegistryMpackVersion(RegistryMpackVersion registryMpackVersion) { + this.registryMpackVersion = registryMpackVersion; + } + + /** + * Get registry mpack version + * @return + */ + @JsonIgnore + public RegistryMpackVersion getRegistryMpackVersion() { + return registryMpackVersion; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisor.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisor.java new file mode 100644 index 0000000..025338d --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisor.java @@ -0,0 +1,341 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.ObjectNotFoundException; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.registry.RegistryRecommendationResponse.RegistryRecommendationResponseBuilder; +import org.apache.ambari.server.registry.RegistryRecommendationResponse.RegistryRecommendations; +import org.apache.ambari.server.registry.RegistryValidationResponse.RegistryValidationItem; +import org.apache.ambari.server.registry.RegistryValidationResponse.RegistryValidationResponseBuilder; +import org.apache.ambari.server.utils.SetUtils; +import org.apache.ambari.server.utils.VersionUtils; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + + +/** + * Registry Advisor + */ +@Singleton +public class RegistryAdvisor { + + private AmbariManagementController managementController; + private long requestId = 0; + + /** + * Given list of all possible mpack bundles, check compatibility and return compatible mpack bundles. + * @param mpackBundles all possible mpack bundles + * @return filtered compatible mpack bundles + */ + private List<Collection<MpackEntry>> filterCompatibleMpackBundles(List<Collection<MpackEntry>> mpackBundles) { + List<Collection<MpackEntry>> compatibleMpackBundles = new LinkedList<>(); + for(Collection<MpackEntry> mpackBundle : mpackBundles) { + if(isCompatibleMpackBundle(mpackBundle)) { + compatibleMpackBundles.add(mpackBundle); + } + } + return compatibleMpackBundles; + } + + /** + * Check if the mpack bundle is compatible. + * @param mpackBundle mpack bundle to check + * @return True if the mpack bundle is compatible, else False + */ + private boolean isCompatibleMpackBundle(Collection<MpackEntry> mpackBundle) { + Map<String, MpackEntry> mpackMap = new HashMap<>(); + for(MpackEntry mvr : mpackBundle) { + mpackMap.put(mvr.getMpackName(), mvr); + } + for(MpackEntry mvr: mpackMap.values()) { + RegistryMpackVersion rmv = mvr.getRegistryMpackVersion(); + List<RegistryMpackCompatiblity> compatiblities = (List<RegistryMpackCompatiblity>) rmv.getCompatibleMpacks(); + if(compatiblities != null && !compatiblities.isEmpty()) { + for(RegistryMpackCompatiblity compatiblity : compatiblities) { + if(mpackMap.containsKey(compatiblity.getName())) { + String selectedVersion = mpackMap.get(compatiblity.getName()).getMpackVersion(); + String minVersion = compatiblity.getMinVersion(); + String maxVersion = compatiblity.getMaxVersion(); + if(minVersion != null && !minVersion.isEmpty() + && VersionUtils.compareVersions(selectedVersion, minVersion) < 0) { + return false; + } + if(maxVersion != null && !maxVersion.isEmpty() + && VersionUtils.compareVersions(selectedVersion, maxVersion) > 0) { + return false; + } + } + } + } + } + return true; + } + + /** + * Get merged list of mpacks that support the selected scenarios. + * @param registry registry to use for getting list of mpacks for selected scenarios + * @param scenarioNames selected scenario names + * @return merged list of scenario mpacks + * @throws AmbariException + */ + private Collection<String> getScenarioMpacks(Registry registry, Collection<String> scenarioNames) + throws AmbariException { + Set<String> scenarioMpackNames = new HashSet<>(); + for(String selectedScenario : scenarioNames) { + RegistryScenario registryScenario = registry.getRegistryScenario(selectedScenario); + for(RegistryScenarioMpack scenarioMpack : registryScenario.getScenarioMpacks()) { + if(!scenarioMpackNames.contains(scenarioMpack.getName())) { + scenarioMpackNames.add(scenarioMpack.getName()); + } + } + } + return scenarioMpackNames; + } + + /** + * Given a list of mpacks, find all mpack versions for each mpack + * @param registry registry to use for getting list of mpack versions + * @param mpackNames list of mpack names + * @return all mpack versions group by mpack + * @throws AmbariException + */ + private List<Collection<MpackEntry>> getAllMpackEntries(Registry registry, Collection<String> mpackNames) + throws AmbariException { + List<Collection<MpackEntry>> allMpackEntries = new LinkedList<>(); + for(String mpackName : mpackNames) { + RegistryMpack registryMpack = registry.getRegistryMpack(mpackName); + List<MpackEntry> mpackEntries = new LinkedList<>(); + for(RegistryMpackVersion registryMpackVersion : registryMpack.getMpackVersions()) { + MpackEntry mpackEntry = new MpackEntry( + registryMpack.getMpackName(), registryMpackVersion.getMpackVersion()); + mpackEntry.setRegistryMpackVersion(registryMpackVersion); + mpackEntries.add(mpackEntry); + } + allMpackEntries.add(mpackEntries); + } + return allMpackEntries; + } + + /** + * Create a mpacks hash map with key [mpack_name]-[mpack_version] + * @param mpackEntries collection of mpacks + * @return hash map of mpacks + */ + private HashMap<String, MpackEntry> convertToMpackVersionsMap(Collection<MpackEntry> mpackEntries) { + HashMap<String, MpackEntry> mpacksMap = new HashMap<>(); + for(MpackEntry mpackEntry : mpackEntries) { + String key = mpackEntry.getMpackName() + "-" + mpackEntry.getMpackVersion(); + mpacksMap.put(key, mpackEntry); + } + return mpacksMap; + } + + /** + * Create a mpacks map with key [mpack_name] + * @param mpackEntries collection of mpacks + * @return hash map of mpacks + */ + private HashMap<String, MpackEntry> convertToMpacksMap(Collection<MpackEntry> mpackEntries) { + HashMap<String, MpackEntry> mpacksMap = new HashMap<>(); + for(MpackEntry mpackEntry : mpackEntries) { + String key = mpackEntry.getMpackName(); + mpacksMap.put(key, mpackEntry); + } + return mpacksMap; + } + + /** + * Find a compatible mpack bundle that matches the selected list of mpacks. + * @param compatibleMpackBundles all compatible mpack bundles + * @param selectedMpacks selected mpacks + * @return compatible mpack bundle + */ + private Collection<MpackEntry> findCompatibleMpackBundle( + List<Collection<MpackEntry>> compatibleMpackBundles, Collection<MpackEntry> selectedMpacks) { + + HashMap<String, MpackEntry> selectedMpacksMap = convertToMpackVersionsMap(selectedMpacks); + for(Collection<MpackEntry> compatibleMpackBundle : compatibleMpackBundles) { + boolean isCompatible = true; + for(MpackEntry mpackEntry: compatibleMpackBundle) { + String key = mpackEntry.getMpackName() + "-" + mpackEntry.getMpackVersion(); + if(!selectedMpacksMap.containsKey(key)) { + isCompatible = false; + break; + } + } + if(isCompatible) { + return compatibleMpackBundle; + } + } + return null; + } + + @Inject + public RegistryAdvisor(AmbariManagementController managementController) { + this.managementController = managementController; + } + + /** + * Returns registry recommendation response based on the request [scenario-mpacks] + * @param request the recommendation request + * @return {@link RegistryRecommendationResponse} for the request + * @throws AmbariException + */ + public synchronized RegistryRecommendationResponse recommend(RegistryAdvisorRequest request) throws AmbariException { + // Get registry + Registry registry = managementController.getRegistry(request.getRegistryId()); + // Get all scenario mpacks + Collection<String> scenarioMpackNames = getScenarioMpacks(registry, request.getSelectedScenarios()); + // Get all mpack versions for each scenario mpack + List<Collection<MpackEntry>> allMpackEntries = getAllMpackEntries(registry, scenarioMpackNames); + // Get all possible mpack bundles + List<Collection<MpackEntry>> allMpackBundles = SetUtils.permutations(allMpackEntries); + // Filter down to compatible mpack bundles + List<Collection<MpackEntry>> compatibleMpackBundles = filterCompatibleMpackBundles(allMpackBundles); + // Order recommentations by versions with latest at pole position. + compatibleMpackBundles.sort(new Comparator<Collection<MpackEntry>>() { + @Override + public int compare(final Collection<MpackEntry> o1, final Collection<MpackEntry> o2) { + int o1Wins = 0; + int o2Wins = 0; + HashMap<String, MpackEntry> o1Map = convertToMpacksMap(o1); + HashMap<String, MpackEntry> o2Map = convertToMpacksMap(o2); + for(Map.Entry<String, MpackEntry> mapEntry : o1Map.entrySet()) { + MpackEntry o1Entry = mapEntry.getValue(); + MpackEntry o2Entry = o2Map.get(mapEntry.getKey()); + int compareResult = VersionUtils.compareVersions(o1Entry.getMpackVersion(), o2Entry.getMpackVersion()); + if(compareResult > 0) { + o1Wins++; + } else if(compareResult < 0) { + o2Wins++; + } + } + // Order in reverse order + return o2Wins - o1Wins; + } + }); + // Create recommendations + RegistryRecommendations recommendations = new RegistryRecommendations(); + recommendations.setMpackBundles(compatibleMpackBundles); + return RegistryRecommendationResponseBuilder.forRegistry(request.getRegistryId()) + .ofType(request.getRequestType()) + .forScenarios(request.getSelectedScenarios()) + .forMpacks(request.getSelectedMpacks()) + .withId(generateRequestId()) + .withRecommendations(recommendations).build(); + } + + /** + * Returns registry validation response based on the request [scenario-mpacks] + * @param request the validation request + * @return {@link RegistryValidationResponse} for the request + * @throws AmbariException + */ + public synchronized RegistryValidationResponse validate(RegistryAdvisorRequest request) throws AmbariException { + Registry registry = managementController.getRegistry(request.getRegistryId()); + List<RegistryValidationItem> validationItems = new LinkedList<>(); + + // Get all mpacks required for the selected scenarios + Collection<String> scenarioMpackNames = getScenarioMpacks(registry, request.getSelectedScenarios()); + + // Validate that all mpacks required for the selected scenarios have been selected + for(String mpackName : scenarioMpackNames) { + boolean isSelected = false; + for(MpackEntry mpackEntry : request.getSelectedMpacks()) { + if(mpackName.equals(mpackEntry.getMpackName())) { + isSelected = true; + break; + } + } + if(!isSelected) { + // Validation failure + String type = "ScenarioMpackValidation"; + String level = "FATAL"; + String message = "Selected mpacks does not contain " + mpackName + + " mpack which is required for supporting the selected scenarios."; + RegistryValidationItem validationItem = new RegistryValidationItem(type, level, message); + validationItems.add(validationItem); + } + } + + // Validate if any selected mpack is not in the registry + List<String> knownSelectedMpackNames = new LinkedList<>(); + for(MpackEntry mpackEntry : request.getSelectedMpacks()) { + try { + RegistryMpack registryMpack = registry.getRegistryMpack(mpackEntry.getMpackName()); + RegistryMpackVersion registryMpackVersion = registryMpack.getMpackVersion(mpackEntry.getMpackVersion()); + knownSelectedMpackNames.add(registryMpack.getMpackName()); + } catch (ObjectNotFoundException ex) { + String type = "UnknownMpackValidation"; + String level = "WARN"; + String mpackFullName = mpackEntry.getMpackName() + "-" + mpackEntry.getMpackVersion(); + String message = "Mpack " + mpackFullName + " not found in the registry. " + + "Cannot validate compatility with other mpacks."; + RegistryValidationItem validationItem = new RegistryValidationItem(type, level, message); + validationItems.add(validationItem); + } + } + + // Get all mpack versions for each known selected mpack + List<Collection<MpackEntry>> allMpackEntries = getAllMpackEntries(registry, knownSelectedMpackNames); + // Get all possible permutations + List<Collection<MpackEntry>> allMpackBundles = SetUtils.permutations(allMpackEntries); + // Filter down to compatible permutations + List<Collection<MpackEntry>> compatibleMpackBundles = filterCompatibleMpackBundles(allMpackBundles); + + // Validate that the selected mpacks is present in a compatible mpack bundle + Collection<MpackEntry> compatibleMpackBundle = findCompatibleMpackBundle(compatibleMpackBundles, request.getSelectedMpacks()); + if(compatibleMpackBundle == null) { + // Selected mpacks are not compatible + String type = "CompatibleMpackValidation"; + String level = "FATAL"; + String message = "Selected mpacks are not compatible and can cause issues during cluster installation."; + RegistryValidationItem validationItem = new RegistryValidationItem(type, level, message); + validationItems.add(validationItem); + } + + return RegistryValidationResponseBuilder.forRegistry(request.getRegistryId()) + .ofType(request.getRequestType()) + .forScenarios(request.getSelectedScenarios()) + .forMpacks(request.getSelectedMpacks()) + .withId(generateRequestId()) + .withValidations(validationItems).build(); + } + + /** + * Generate registry advisor request id + * TODO: Store registry advisor requests in database. + * @return + */ + public long generateRequestId() { + requestId += 1; + return requestId; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorRequest.java new file mode 100644 index 0000000..72e9279 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorRequest.java @@ -0,0 +1,131 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import java.util.Arrays; +import java.util.List; + +import org.apache.ambari.server.exceptions.RegistryAdvisorException; + +/** + * Represents a registry advisor request + */ +public class RegistryAdvisorRequest { + private Long registryId; + private RegistryAdvisorRequestType requestType; + private List<String> selectedScenarios; + private List<MpackEntry> selectedMpacks; + + /** + * Constructor + * @param registryId ID of software registry + */ + public RegistryAdvisorRequest(Long registryId) { + this.registryId = registryId; + } + + public Long getRegistryId() { + return registryId; + } + + public RegistryAdvisorRequestType getRequestType() { + return requestType; + } + + public List<String> getSelectedScenarios() { + return selectedScenarios; + } + + public List<MpackEntry> getSelectedMpacks() { + return selectedMpacks; + } + + /** + * Registry advisor request builder + */ + public static class RegistryAdvisorRequestBuilder { + RegistryAdvisorRequest instance; + + private RegistryAdvisorRequestBuilder(Long registryId) { + this.instance = new RegistryAdvisorRequest(registryId); + } + + public static RegistryAdvisorRequestBuilder forRegistry(Long registryId) { + return new RegistryAdvisorRequestBuilder(registryId); + } + + public RegistryAdvisorRequestBuilder ofType(RegistryAdvisorRequestType requestType) { + this.instance.requestType = requestType; + return this; + } + + public RegistryAdvisorRequestBuilder forScenarios(List<String> selectedScenarios) { + this.instance.selectedScenarios = selectedScenarios; + return this; + } + + public RegistryAdvisorRequestBuilder forMpacks(List<MpackEntry> selectedMpacks) { + this.instance.selectedMpacks = selectedMpacks; + return this; + } + + public RegistryAdvisorRequest build() { + return this.instance; + } + } + + /** + * + */ + public enum RegistryAdvisorRequestType { + SCENARIO_MPACKS("scenario-mpacks"); + + private String type; + + RegistryAdvisorRequestType(String type) { + this.type = type; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return type; + } + + /** + * Get registry advisor request type from string + * @param text string input + * @return {@link RegistryAdvisorRequestType} + * @throws RegistryAdvisorException + */ + public static RegistryAdvisorRequestType fromString(String text) throws RegistryAdvisorException { + if (text != null) { + for (RegistryAdvisorRequestType next : RegistryAdvisorRequestType.values()) { + if (text.equalsIgnoreCase(next.type)) { + return next; + } + } + } + throw new RegistryAdvisorException(String.format( + "Unknown request type: %s, possible values: %s", text, + Arrays.toString(RegistryAdvisorRequestType.values()))); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorResponse.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorResponse.java new file mode 100644 index 0000000..38db4c3 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryAdvisorResponse.java @@ -0,0 +1,121 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import java.util.List; + +import org.apache.ambari.server.registry.RegistryAdvisorRequest.RegistryAdvisorRequestType; + +/** + * Abstract registry advisor response POJO + */ +public abstract class RegistryAdvisorResponse { + private Long id; + private Long registryId; + private RegistryAdvisorRequest.RegistryAdvisorRequestType requestType; + private List<String> selectedScenarios; + private List<MpackEntry> selectedMpacks; + + /** + * Set registry advisor request id + * @param id + */ + public void setId(Long id) { + this.id = id; + } + + /** + * Get registry advisor request id + * @return + */ + public Long getId() { + return id; + } + + /** + * Set registry id + * @param registryId + */ + public void setRegistryId(Long registryId) { + this.registryId = registryId; + } + + /** + * Get registry id + * @return + */ + public Long getRegistryId() { + return registryId; + } + + /** + * Set request type + * @param requestType + */ + public void setRequestType(RegistryAdvisorRequestType requestType) { + this.requestType = requestType; + } + + /** + * Get request type + * @return + */ + public RegistryAdvisorRequestType getRequestType() { + return requestType; + } + + /** + * Set selected scenarios + * @param selectedScenarios + */ + public void setSelectedScenarios(List<String> selectedScenarios) { + this.selectedScenarios = selectedScenarios; + } + + /** + * Get selected scenarios + * @return + */ + public List<String> getSelectedScenarios() { + return selectedScenarios; + } + + /** + * Set selected mpacks + * @param selectedMpacks + */ + public void setSelectedMpacks(List<MpackEntry> selectedMpacks) { + this.selectedMpacks = selectedMpacks; + } + + /** + * Get selected mpacks + * @return + */ + public List<MpackEntry> getSelectedMpacks() { + return selectedMpacks; + } + + /** + * Constructor + * @param registryId ID of software registry + */ + public RegistryAdvisorResponse(Long registryId) { + this.registryId = registryId; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryRecommendationResponse.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryRecommendationResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryRecommendationResponse.java new file mode 100644 index 0000000..923b409 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryRecommendationResponse.java @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import java.util.Collection; +import java.util.List; + +import org.apache.ambari.server.registry.RegistryAdvisorRequest.RegistryAdvisorRequestType; + +/** + * Registry recommendation response + */ +public class RegistryRecommendationResponse extends RegistryAdvisorResponse { + + + private RegistryRecommendations recommendations; + + /** + * Constructor + * @param registryId ID of software registry + */ + public RegistryRecommendationResponse(Long registryId) { + super(registryId); + } + + /** + * Set recommendations + * @param recommendations + */ + public void setRecommendations(RegistryRecommendations recommendations) { + this.recommendations = recommendations; + } + + /** + * Get recommentations + * @return + */ + public RegistryRecommendations getRecommendations() { + return recommendations; + } + + /** + * Registry advisor request builder + */ + public static class RegistryRecommendationResponseBuilder { + RegistryRecommendationResponse instance; + + private RegistryRecommendationResponseBuilder(Long registryId) { + this.instance = new RegistryRecommendationResponse(registryId); + } + + public static RegistryRecommendationResponseBuilder forRegistry(Long registryId) { + return new RegistryRecommendationResponseBuilder(registryId); + } + + public RegistryRecommendationResponseBuilder ofType(RegistryAdvisorRequestType requestType) { + this.instance.setRequestType(requestType); + return this; + } + + public RegistryRecommendationResponseBuilder forScenarios(List<String> selectedScenarios) { + this.instance.setSelectedScenarios(selectedScenarios); + return this; + } + + public RegistryRecommendationResponseBuilder forMpacks(List<MpackEntry> selectedMpacks) { + this.instance.setSelectedMpacks(selectedMpacks); + return this; + } + + public RegistryRecommendationResponseBuilder withId(Long id) { + this.instance.setId(id); + return this; + } + + public RegistryRecommendationResponseBuilder withRecommendations(RegistryRecommendations recommendations) { + this.instance.recommendations = recommendations; + return this; + } + + public RegistryRecommendationResponse build() { + return this.instance; + } + } + + /** + * Registry recommendations + */ + public static class RegistryRecommendations { + public List<Collection<MpackEntry>> mpackBundles; + + public void setMpackBundles(List<Collection<MpackEntry>> mpackBundles) { + this.mpackBundles = mpackBundles; + } + + public List<Collection<MpackEntry>> getMpackBundles() { + return mpackBundles; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryValidationResponse.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryValidationResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryValidationResponse.java new file mode 100644 index 0000000..ed3b137 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/registry/RegistryValidationResponse.java @@ -0,0 +1,124 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.registry; + +import java.util.List; + +import org.apache.ambari.server.registry.RegistryAdvisorRequest.RegistryAdvisorRequestType; + +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Registry validation response + */ +public class RegistryValidationResponse extends RegistryAdvisorResponse { + + @JsonProperty + private List<RegistryValidationItem> items; + + public List<RegistryValidationItem> getItems() { + return items; + } + + public void setItems(List<RegistryValidationItem> items) { + this.items = items; + } + + /** + * Constructor + * + * @param registryId ID of software registry + */ + public RegistryValidationResponse(final Long registryId) { + super(registryId); + } + + /** + * Registry advisor request builder + */ + public static class RegistryValidationResponseBuilder { + RegistryValidationResponse instance; + + private RegistryValidationResponseBuilder(Long registryId) { + this.instance = new RegistryValidationResponse(registryId); + } + + public static RegistryValidationResponseBuilder forRegistry(Long registryId) { + return new RegistryValidationResponseBuilder(registryId); + } + + public RegistryValidationResponseBuilder ofType(RegistryAdvisorRequestType requestType) { + this.instance.setRequestType(requestType); + return this; + } + + public RegistryValidationResponseBuilder forScenarios(List<String> selectedScenarios) { + this.instance.setSelectedScenarios(selectedScenarios); + return this; + } + + public RegistryValidationResponseBuilder forMpacks(List<MpackEntry> selectedMpacks) { + this.instance.setSelectedMpacks(selectedMpacks); + return this; + } + + public RegistryValidationResponseBuilder withId(Long id) { + this.instance.setId(id); + return this; + } + + public RegistryValidationResponseBuilder withValidations(List<RegistryValidationItem> items) { + this.instance.items = items; + return this; + } + + public RegistryValidationResponse build() { + return this.instance; + } + } + + /** + * Registry validation item + */ + public static class RegistryValidationItem { + @JsonProperty + private String type; + + @JsonProperty + private String level; + + @JsonProperty + private String message; + + public RegistryValidationItem(String type, String level, String message) { + this.type = type; + this.level = level; + this.message = message; + } + public String getType() { + return type; + } + public String getLevel() { + return level; + } + public String getMessage() { + return message; + } + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/332053bc/ambari-server/src/main/java/org/apache/ambari/server/utils/SetUtils.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/SetUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/SetUtils.java index 5aeb02d..7748a11 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/utils/SetUtils.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/SetUtils.java @@ -18,9 +18,11 @@ package org.apache.ambari.server.utils; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -59,4 +61,42 @@ public class SetUtils { } return subsets; } + + /** + * Create all possible permutations of a list of collections. + * @param collections List of collections + * @param <T> + * @return Permutations between the collections + */ + public static <T> List<Collection<T>> permutations(List<Collection<T>> collections) { + if(collections == null || collections.isEmpty()) { + return Collections.emptyList(); + } else { + List<Collection<T>> results = new LinkedList<>(); + permutationsImpl(collections, results, 0, new LinkedList<T>()); + return results; + } + } + + /** + * Permutations implementation + * @param original + * @param results + * @param depth + * @param current + * @param <T> + */ + private static <T> void permutationsImpl(List<Collection<T>> original, + List<Collection<T>> results, int depth, Collection<T> current) { + if(depth == original.size()) { + results.add(current); + return; + } + Collection<T> currentList = original.get(depth); + for(T element : currentList) { + List<T> copyList = new LinkedList<T>(current); + copyList.add(element); + permutationsImpl(original, results, depth + 1, copyList); + } + } }
