/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.catalog; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.emc.sa.descriptor.ServiceDescriptor; import com.emc.sa.descriptor.ServiceDescriptors; import com.emc.storageos.db.client.model.uimodels.CatalogCategory; import com.emc.storageos.db.client.model.uimodels.CatalogService; import com.emc.storageos.db.client.model.uimodels.Order; import com.emc.sa.model.dao.ModelClient; import com.emc.sa.model.util.SortedIndexUtils; import com.emc.sa.util.Messages; import com.emc.sa.util.ServiceIdPredicate; import com.emc.storageos.db.client.model.NamedURI; import com.google.common.collect.Lists; @Component public class CatalogCategoryManagerImpl implements CatalogCategoryManager { private static final Logger log = Logger.getLogger(CatalogCategoryManagerImpl.class); @Autowired private ModelClient client; @Autowired private CatalogServiceManager catalogServiceManager; @Autowired private ServiceDescriptors serviceDescriptors; private Messages MESSAGES = new Messages(CatalogBuilder.class, "default-catalog"); public void upgradeCatalog(URI tenantId) throws IOException { CatalogCategory rootCategory = getOrCreateRootCategory(tenantId); com.emc.sa.catalog.CategoryDef newCatalog = CatalogBuilder.readCatalogDef(getDefaultCatalog()); log.info(String.format("Updating Service catalog for tenant %s", tenantId)); upgradeCategory(rootCategory, newCatalog); rootCategory.setVersion(newCatalog.version); client.save(rootCategory); } public CatalogCategory getOrCreateRootCategory(URI tenantId) { CatalogCategory root = null; root = client.catalogCategories().getRootCategory(tenantId.toString()); if (root == null) { root = createDefaultCatalog(tenantId); } return root; } public void restoreDefaultCatalog(URI tenant) throws IOException { // Delete old catalog CatalogCategory catalog = getOrCreateRootCategory(tenant); deleteCatalogCategory(catalog); // Rebuild catalog catalog = getOrCreateRootCategory(tenant); CatalogBuilder builder = new CatalogBuilder(client, serviceDescriptors); builder.clearCategory(catalog); builder.buildCatalog(tenant.toString(), getDefaultCatalog()); } private CatalogCategory createDefaultCatalog(URI tenant) { loadCatalog(tenant); return client.catalogCategories().getRootCategory(tenant.toString()); } private void loadCatalog(URI tenant) { try { log.info("Loading default catalog"); new CatalogBuilder(client, serviceDescriptors).buildCatalog(tenant.toString(), getDefaultCatalog()); } catch (IOException e) { log.error("Failed to populate default catalog", e); } catch (RuntimeException e) { log.error("Failed to populate default catalog", e); } } public boolean isCatalogUpdateAvailable(URI tenantId) { try { CatalogCategory rootCategory = getOrCreateRootCategory(tenantId); if (rootCategory.getVersion() == null) { return true; } else { String catalogHash = CatalogBuilder.getCatalogHash(getDefaultCatalog()); return !rootCategory.getVersion().equals(catalogHash); } } catch (IOException e) { log.error("Reading default catalog file", e); return false; } } private boolean isServiceUsedForOrders(String tenantId, CatalogService service) { String serviceId = service.getId().toString(); List<Order> orders = client.orders().findAll(tenantId); return CollectionUtils.exists(orders, new ServiceIdPredicate(serviceId)); } private InputStream getDefaultCatalog() { return getClass().getResourceAsStream("default-catalog.json"); } /** * Updates the category, adding and removing out off date sub categories and services * (Warning: This is recursive) */ private void upgradeCategory(CatalogCategory currentCategory, com.emc.sa.catalog.CategoryDef newCategory) { if (newCategory.categories != null) { List<CatalogCategory> subCategories = client.catalogCategories().findSubCatalogCategories(currentCategory.getId()); for (com.emc.sa.catalog.CategoryDef newSubCategory : newCategory.categories) { String label = StringUtils.deleteWhitespace(getMessage(getLabel(newSubCategory))); CatalogCategory currentSubCategory = findSubCategory(subCategories, label); if (currentSubCategory == null) { log.info(String.format("CREATING Missing Category : %s for tenant:%s", label, currentCategory.getTenant())); createCategory(currentCategory.getTenant(), newSubCategory, currentCategory); } else { upgradeCategory(currentSubCategory, newSubCategory); } } } upgradeServices(currentCategory, newCategory); } private void upgradeServices(CatalogCategory currentCategory, com.emc.sa.catalog.CategoryDef newCategory) { List<CatalogService> services = client.catalogServices().findByCatalogCategory(currentCategory.getId()); // Add or Update Missing Services if (newCategory.services != null) { for (com.emc.sa.catalog.ServiceDef newService : newCategory.services) { List<CatalogService> matchingServices = findServices(services, newService.baseService); if (matchingServices != null && !matchingServices.isEmpty()) { updateMatchingServices(currentCategory, matchingServices, newService); } else { ServiceDescriptor descriptor = serviceDescriptors.getDescriptor(Locale.getDefault(), newService.baseService); String label = ""; if (descriptor != null) { label = StringUtils.deleteWhitespace(StringUtils.defaultString(getMessage(getLabel(newService)), descriptor.getTitle())); } log.info(String.format("CREATING Missing Service %s: for tenant: %s", label, currentCategory.getTenant())); catalogServiceManager.createCatalogService(newService, currentCategory); } } } // Remove Old Services for (CatalogService service : services) { ServiceDescriptor serviceDescriptor = null; try { serviceDescriptor = serviceDescriptors.getDescriptor(Locale.getDefault(), service.getBaseService()); } catch (IllegalStateException ese) { // getDescriptor throws exception when no descriptor found } if (serviceDescriptor == null) { log.info(String.format("REMOVING Service '%s' as base service '%s' no longer exists for tenant:%s", service.getTitle(), service.getBaseService(), currentCategory.getTenant())); catalogServiceManager.deleteCatalogService(service); } } } private <K, V> boolean mapEquals(Map<K, V> m1, Map<K, V> m2) { if ((m1 == null || m1.isEmpty()) && (m2 == null || m2.isEmpty())) { return true; } else if (m1 != null && m2 != null && m1.equals(m2)) { return true; } return false; } private void updateMatchingServices(CatalogCategory currentCategory, List<CatalogService> services, ServiceDef newService) { int pristineService = 0; for (CatalogService service : services) { if (isMatch(service, newService)) { if (pristineService == 0) { log.info(String.format("Updating Existing Matching Service %s: for tenant: %s", service.getLabel(), currentCategory.getTenant())); ServiceDescriptor descriptor = serviceDescriptors.getDescriptor(Locale.getDefault(), newService.baseService); if (descriptor != null) { service.setLabel(StringUtils.deleteWhitespace(StringUtils.defaultString(getMessage(getLabel(newService)), descriptor.getTitle()))); service.setTitle(StringUtils.defaultString(getMessage(newService.title), descriptor.getTitle())); service.setDescription(StringUtils.defaultString(getMessage(newService.description), descriptor.getDescription())); } service.setImage(newService.image); catalogServiceManager.updateCatalogService(service, catalogServiceManager.getCatalogServiceFields(service.getId())); pristineService++; } else { log.info(String.format("Removing Duplicate Service %s: for tenant: %s", service.getLabel(), currentCategory.getTenant())); catalogServiceManager.deleteCatalogService(service); } } } } private boolean isMatch(CatalogService service, ServiceDef newService) { Map<String, String> serviceFields = catalogServiceManager .getLockedFields(service.getId()); Map<String, String> newServiceFields = newService.lockFields; if (mapEquals(serviceFields, newServiceFields) && !service.getApprovalRequired() && !service.getExecutionWindowRequired() && (service.getAcls() == null || !service.getAcls().isEmpty())) { return true; } return false; } private List<CatalogService> findServices(List<CatalogService> services, String baseService) { List<CatalogService> matchingServices = Lists.newArrayList(); for (CatalogService service : services) { if (StringUtils.equals(service.getBaseService(), baseService)) { matchingServices.add(service); } } return matchingServices; } private CatalogCategory findSubCategory(List<CatalogCategory> categories, String label) { for (CatalogCategory subCategory : categories) { if (subCategory.getLabel().equals(label)) { return subCategory; } } return null; } private CatalogCategory createCategory(String tenant, CategoryDef def, CatalogCategory parentCategory) { CatalogBuilder builder = new CatalogBuilder(client, serviceDescriptors); NamedURI namedUri = new NamedURI(parentCategory.getId(), parentCategory.getLabel()); CatalogCategory newCategory = builder.createCategory(tenant, def, namedUri); newCategory.setSortedIndex(null); client.save(newCategory); return newCategory; } /** * Get catalog category object from id * * @param id the URN of a catalog category * @return */ public CatalogCategory getCatalogCategoryById(URI id) { if (id == null) { return null; } CatalogCategory catalogCategory = client.catalogCategories().findById(id); return catalogCategory; } public void createCatalogCategory(CatalogCategory catalogCategory) { if (catalogCategory.getSortedIndex() == null) { catalogCategory.setSortedIndex(SortedIndexUtils.getNextSortedIndex(catalogCategory, client)); } client.save(catalogCategory); } public void updateCatalogCategory(CatalogCategory catalogCategory) { if (catalogCategory.getSortedIndex() == null) { catalogCategory.setSortedIndex(SortedIndexUtils.getNextSortedIndex(catalogCategory, client)); } client.save(catalogCategory); } public void deleteCatalogCategory(CatalogCategory catalogCategory) { deleteCategoryContents(catalogCategory); client.delete(catalogCategory); } private void deleteCategoryContents(CatalogCategory catalogCategory) { List<CatalogCategory> categories = getSubCategories(catalogCategory.getId()); for (CatalogCategory subCategory : categories) { deleteCatalogCategory(subCategory); } List<CatalogService> services = catalogServiceManager.getCatalogServices(catalogCategory.getId()); for (CatalogService service : services) { catalogServiceManager.deleteCatalogService(service); } } public List<CatalogCategory> getSubCategories(URI parentCatalogCategoryId) { List<CatalogCategory> categories = client.catalogCategories().findSubCatalogCategories(parentCatalogCategoryId); SortedIndexUtils.sort(categories); return categories; } public void moveUpCatalogCategory(URI catalogCategoryId) { CatalogCategory catalogCategory = getCatalogCategoryById(catalogCategoryId); if (catalogCategory != null) { SortedIndexUtils.moveUp(catalogCategory, client); } } public void moveDownCatalogCategory(URI catalogCategoryId) { CatalogCategory catalogCategory = getCatalogCategoryById(catalogCategoryId); if (catalogCategory != null) { SortedIndexUtils.moveDown(catalogCategory, client); } } protected String getLabel(CategoryDef def) { return StringUtils.defaultString(def.label, def.title); } protected String getLabel(ServiceDef def) { return StringUtils.defaultString(def.label, def.title); } protected String getMessage(String key) { try { return (key != null) ? MESSAGES.get(key) : null; } catch (MissingResourceException e) { return key; } } }