/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package controllers.catalog; import static com.emc.vipr.client.core.util.ResourceUtils.uri; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; import play.Logger; import play.data.validation.Validation; import play.mvc.Controller; import play.mvc.Util; import play.mvc.With; import util.CatalogServiceUtils; import util.ServiceDescriptorUtils; import util.TimeUtils; import util.descriptor.ServiceFieldValidator; import com.emc.sa.descriptor.ServiceField; import com.emc.sa.util.TextUtils; import com.emc.vipr.model.catalog.CatalogServiceFieldRestRep; import com.emc.vipr.model.catalog.CatalogServiceRestRep; import com.emc.vipr.model.catalog.OrderCreateParam; import com.emc.vipr.model.catalog.Parameter; import com.emc.vipr.model.catalog.ScheduleCycleType; import com.emc.vipr.model.catalog.ScheduleInfo; import com.emc.vipr.model.catalog.ScheduledEventCreateParam; import com.emc.vipr.model.catalog.ServiceDescriptorRestRep; import com.emc.vipr.model.catalog.ServiceFieldGroupRestRep; import com.emc.vipr.model.catalog.ServiceFieldModalRestRep; import com.emc.vipr.model.catalog.ServiceFieldRestRep; import com.emc.vipr.model.catalog.ServiceFieldTableRestRep; import com.emc.vipr.model.catalog.ServiceItemRestRep; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import controllers.Common; import controllers.util.Models; /** * Base order controller that contains operations for executing orders. This is * subclassed for the API or main order form. * * @author Chris Dail */ @With(Common.class) public class OrderExecution extends Controller { /* * the services which support scheduler, and can be run re-occurencely * key -- base service name * value -- the field name, which can apply {datetime} to. */ private static HashMap<String, String> SCHEDULED_SERVICE = new HashMap<String, String>(); static { SCHEDULED_SERVICE.put("CreateCloneOfApplication", "applicationCopySets"); SCHEDULED_SERVICE.put("CreateSnapshotOfApplication", "applicationCopySets"); SCHEDULED_SERVICE.put("CreateBlockSnapshot", "name"); SCHEDULED_SERVICE.put("CreateFullCopy", "name"); SCHEDULED_SERVICE.put("CreateFileSnapshot", "name"); } @Util public static Map<String, String> parseParameters(CatalogServiceRestRep service, ServiceDescriptorRestRep descriptor) { Map<String, String> parameters = Maps.newLinkedHashMap(); Map<String, String> locked = getLockedFields(service); addFieldValues(service, descriptor.getItems(), parameters, locked); return parameters; } /** * Adds the field values from the service item container to the map. * * @param service * the service. * @param container * the service item container. * @param values * the map holding the values. * @param locked * all locked values for the service. */ private static void addFieldValues(CatalogServiceRestRep service, Collection<? extends ServiceItemRestRep> items, Map<String, String> values, Map<String, String> locked) { for (ServiceItemRestRep item : items) { if (item instanceof ServiceFieldTableRestRep) { addColumnValues(service, (ServiceFieldTableRestRep) item, values, locked); } else if (item instanceof ServiceFieldGroupRestRep) { addFieldValues(service, ((ServiceFieldGroupRestRep) item).getItems(), values, locked); } else if (item instanceof ServiceFieldModalRestRep) { addFieldValues(service, ((ServiceFieldModalRestRep) item).getItems(), values, locked); } else if (item instanceof ServiceFieldRestRep) { ServiceFieldRestRep field = (ServiceFieldRestRep) item; String value = getFieldValue(field); if (locked.containsKey(field.getName())) { value = locked.get(field.getName()); } if (value != null) { values.put(field.getName(), value); } List<String> fieldValues = TextUtils.parseCSV(value); if (fieldValues.isEmpty() && field.isRequired()) { Validation.required(field.getName(), null); } for (String fieldValue : fieldValues) { ServiceFieldValidator.validateField(service, field, fieldValue); } } } } /** * Adds all column values for the given table. * * @param service * the catalog service. * @param table * the table of fields. * @param values * the map holding the values. * @param locked * all locked values for the service. */ private static void addColumnValues(CatalogServiceRestRep service, ServiceFieldTableRestRep table, Map<String, String> values, Map<String, String> locked) { List<ServiceFieldRestRep> fields = ServiceDescriptorUtils.getAllFieldList(table.getItems()); int rowCount = 0; for (ServiceFieldRestRep field : fields) { if (!locked.containsKey(field.getName())) { String[] columns = getColumnValue(table, field); rowCount = Math.max(rowCount, columns.length); } } for (ServiceFieldRestRep field : fields) { String[] columns = new String[rowCount]; if (locked.containsKey(field.getName())) { String lockedValue = locked.get(field.getName()); for (int i = 0; i < columns.length; i++) { columns[i] = lockedValue; } } else { String[] col = getColumnValue(table, field); System.arraycopy(col, 0, columns, 0, col.length); } for (int i = 0; i < columns.length; i++) { String prefix = table.getName() + "[" + i + "]"; ServiceFieldValidator.validateField(service, prefix, field, columns[i]); } values.put(field.getName(), TextUtils.formatCSV(columns)); } } /** * Gets all locked fields from the catalog service. * * @param service * the catalog service. * @return the map of locked field values. */ private static Map<String, String> getLockedFields(CatalogServiceRestRep service) { Map<String, String> fields = Maps.newLinkedHashMap(); for (CatalogServiceFieldRestRep field : service.getCatalogServiceFields()) { String value = getLockedValue(field); if (value != null) { fields.put(field.getName(), value); } } return fields; } private static String getFieldValue(ServiceFieldRestRep field) { if (params._contains(field.getName())) { String[] values = params.getAll(field.getName()); if (values != null) { return TextUtils.formatCSV(values); } } return null; } /** * Gets the submitted value for the column field from the HTTP params. The * parameters are named: <tt><<i>table.name</i>>[<i>i</i>].<<i>field.name</i>></tt> * * @param table * the table containing the field. * @param field * the field. * @return the values for the column. */ private static String[] getColumnValue(ServiceFieldTableRestRep table, ServiceFieldRestRep field) { List<String> values = Lists.newArrayList(); Pattern pattern = Pattern.compile(table.getName() + "\\[(\\d+)\\]." + field.getName()); for (String name : params.data.keySet()) { Matcher match = pattern.matcher(name); if (match.matches()) { int index = Integer.valueOf(match.group(1)); for (int i = values.size(); i <= index; i++) { values.add(null); } values.set(index, params.get(name)); } } return values.toArray(new String[values.size()]); } /** * Gets the locked value of the field if it is overridden, otherwise returns * null. * * @param field * the field. * @return the locked value, or null. */ private static String getLockedValue(CatalogServiceFieldRestRep field) { if (Boolean.TRUE.equals(field.getOverride()) && StringUtils.isNotBlank(field.getValue())) { return field.getValue(); } else { return null; } } @Util public static OrderCreateParam createOrder(CatalogServiceRestRep service, ServiceDescriptorRestRep descriptor, Map<String, String> parameters) { OrderCreateParam order = new OrderCreateParam(); order.setTenantId(uri(Models.currentAdminTenant())); order.setCatalogService(service.getId()); List<Parameter> orderParameters = Lists.newArrayList(); List<ServiceFieldRestRep> fields = ServiceDescriptorUtils.getAllFieldList(descriptor.getItems()); for (ServiceFieldRestRep field : fields) { String value = parameters.get(field.getName()); if (StringUtils.isNotBlank(value)) { orderParameters.add(createOrderParameter(field, value)); } } order.setParameters(orderParameters); return order; } private static Parameter createOrderParameter(ServiceFieldRestRep field, String value) { Parameter parameter = new Parameter(); parameter.setLabel(field.getName()); if (value != null) { parameter.setValue(value.trim()); } else { parameter.setValue(value); // NOSONAR // ("Suppressing Sonar violation of Load of known null value. Value can be null and it needs to be set when null") } parameter.setUserInput(true); if (StringUtils.equals(field.getType(), ServiceField.TYPE_PASSWORD)) { parameter.setEncrypted(true); } return parameter; } protected static ScheduledEventCreateParam createScheduledOrder(OrderCreateParam orderParam) { if (!isSchedulerEnabled()) { return null; } ScheduleInfo scheduleInfo = new ScheduleInfo(); String cycleFrequency = params.get("scheduler.cycleFrequency"); if (cycleFrequency != null) { scheduleInfo.setCycleFrequency(Integer.parseInt(cycleFrequency)); } else { scheduleInfo.setCycleFrequency(1); } String cycleType = params.get("scheduler.cycleType"); if (cycleType != null) { ScheduleCycleType cycleTypeEnum = ScheduleCycleType.valueOf(cycleType); scheduleInfo.setCycleType(cycleTypeEnum); List<String> sectionsInCycleList = Lists.newArrayList(); if (cycleTypeEnum == ScheduleCycleType.WEEKLY) { String sectionsInCycle = params.get("scheduler.dayOfWeek"); sectionsInCycleList.add(sectionsInCycle); } else if(cycleTypeEnum == ScheduleCycleType.MONTHLY) { String sectionsInCycle = params.get("scheduler.dayOfMonth"); sectionsInCycleList.add(sectionsInCycle); } scheduleInfo.setSectionsInCycle(sectionsInCycleList); } else { scheduleInfo.setCycleType(ScheduleCycleType.DAILY); } String currentTimezoneOffsetInMins = params.get("scheduler.currentTimezoneOffsetInMins"); Integer timezoneOffset = Integer.parseInt(currentTimezoneOffsetInMins); String startDate = params.get("scheduler.startDate"); String startTime = params.get("scheduler.startTime"); String isoDateTimeStr = String.format("%sT%s", startDate, startTime); DateTime startDateTime = DateTime.parse(isoDateTimeStr, ISODateTimeFormat.localDateOptionalTimeParser().withZone(TimeUtils.getLocalTimeZone(timezoneOffset))); startDateTime = startDateTime.withZone(DateTimeZone.UTC); scheduleInfo.setHourOfDay(startDateTime.getHourOfDay()); scheduleInfo.setMinuteOfHour(startDateTime.getMinuteOfHour()); scheduleInfo.setStartDate(String.format("%d-%02d-%02d", startDateTime.getYear(), startDateTime.getMonthOfYear(), startDateTime.getDayOfMonth())); String recurrence = params.get("scheduler.recurrence"); int recurrenceNum = 1; if (recurrence != null) { recurrenceNum = Integer.parseInt(recurrence); if (recurrenceNum == -1) { String range = params.get("scheduler.rangeOfRecurrence"); recurrenceNum = Integer.parseInt(range); } } scheduleInfo.setReoccurrence(recurrenceNum); /* * if reoccurence number large than 1, we must make sure the name contains patten {datetime}, * with the pattern in the name, vipr know how to generate dynamic name for each snaphot/fullcopy. */ if (recurrenceNum != 1) { List<Parameter> parameters = orderParam.getParameters(); CatalogServiceRestRep service = CatalogServiceUtils.getCatalogService(orderParam.getCatalogService()); Logger.info("creating order with parameter for: " + service.getBaseService()); String nameToValidate = SCHEDULED_SERVICE.get(service.getBaseService()); for (Parameter parameter : parameters) { if (parameter.getLabel().equals(nameToValidate) && !parameter.getValue().contains("{datetime}")) { Validation.addError(nameToValidate, "need to add patten '{datetime}' in the name for reoccuring scheduled operation"); } Logger.info(parameter.getLabel() + " = " + parameter.getValue() + ", " + parameter.getFriendlyLabel() + " = " + parameter.getFriendlyValue()); } } String maxNumOfCopies = params.get("scheduler.maxNumOfCopies"); if (maxNumOfCopies != null) { orderParam.setAdditionalScheduleInfo(maxNumOfCopies); } scheduleInfo.setDurationLength(3600); ScheduledEventCreateParam eventParam = new ScheduledEventCreateParam(); eventParam.setOrderCreateParam(orderParam); eventParam.setScheduleInfo(scheduleInfo); return eventParam; } protected static boolean isSchedulerEnabled() { if (params._contains("schedulerEnabled")) { return Boolean.valueOf(params.get("schedulerEnabled")); } return false; } }