diff options
Diffstat (limited to 'cmso-optimizer')
7 files changed, 846 insertions, 1 deletions
diff --git a/cmso-optimizer/etc/config/optimizer.properties b/cmso-optimizer/etc/config/optimizer.properties index ac39ec8..641bbeb 100644 --- a/cmso-optimizer/etc/config/optimizer.properties +++ b/cmso-optimizer/etc/config/optimizer.properties @@ -46,4 +46,9 @@ logging.level.org.hibernate=TRACE cmso.topology.create.request.url=http://127.0.0.1:7998/topology/v1/current cmso.ticket.create.request.url=http://127.0.0.1:7999/ticketmgt/v1/activetickets -cmso.local.policy.folder=data/policies
\ No newline at end of file +cmso.local.policy.folder=data/policies + +cmso.minizinc.command.exe="C:/Program Files/MiniZinc IDE (bundled)/minizinc.exe" +cmso.minizinc.command.solver=OSICBC +cmso.minizinc.command.timelimit=60000 +cmso.minizinc.command.mzn=scripts/minizinc/generic_attributes.mzn diff --git a/cmso-optimizer/pom.xml b/cmso-optimizer/pom.xml index b609de5..2ede9e7 100644 --- a/cmso-optimizer/pom.xml +++ b/cmso-optimizer/pom.xml @@ -141,6 +141,11 @@ <artifactId>httpclient</artifactId> </dependency> <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.6</version> + </dependency> + <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> diff --git a/cmso-optimizer/scripts/minizinc/generic_attributes.mzn b/cmso-optimizer/scripts/minizinc/generic_attributes.mzn new file mode 100644 index 0000000..af38df9 --- /dev/null +++ b/cmso-optimizer/scripts/minizinc/generic_attributes.mzn @@ -0,0 +1,199 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% element scheduling problem +%% Modified on Mar 08, 2019 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Parameters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Required (core) parameters +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Number of elements. +int: numElements; + +% Number of loaders/engineers. +int: numLoaders; + +% Index of last possible time-slot for scheduling These time slots can be +% minutes, hours, or nights. E.g., if scheduling happens at the granularity of +% nights, 1st night is counted as 1, 2nd night counted as 2, and so on. OTOH, +% if we have 6 hours each night and we are scheduling at the granularity of +% hours, 1st hour of 1st night is 1, 1st hour of 2nd night is 7, and so on. +int: maxTime; + +% TRUE, if i-th element does NOT have a conflict on j-th timeslot/night. +array[1..numElements, 1..maxTime] of bool: noConflict; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Optional parameters (defined via policy) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Each attribute is composed of 3 parameters that must be supplied. When an +% attribute is selected, all parameters related to it must be given. + +%%%%%%%%%%%%%%%%%%%% +% Timeslots / nights capacities +%%%%%%%%%%%%%%%%%%%% + +% Maximum number of elements that can be scheduled on j-th timeslot/night. +array[1..maxTime] of 0..numElements: elementSlotCapacity; + +%%%%%%%%%%%%%%%%%%%% +% Loader capacities +%%%%%%%%%%%%%%%%%%%% + +% Maximum number of elements that can be assigned to the j-th loader per +% timeslot/night. +array[1..numLoaders, 1..maxTime] of 0..numElements: loaderCapacity; + +%%%%%%%%%%%%%%%%%%%% +% Attribute matrix +%%%%%%%%%%%%%%%%%%%% + +% Number of attributes for wach node, e.g., hardware, software, oss, market, +% pool, etc. +int: numAttributes; + +% Assume that i-th attribute has a range 0..attribute_range[i]. +% Assume that 0 indicates NA. E.g, we may have pools and a node +% that does not belong to any pools will have 'pool attribute = 0 +% when we write constraints, we only consider attribute values >= 1. +array[1..numAttributes] of int: attributesRange; + +% The attribute matrix that holds values of each attribute for a element. +array[1..numElements, 1..numAttributes] of int: attributes; + +% Maximum number of nodes per time-slot that match on a given attribute. +array[1..numAttributes, 1..maxTime] of int: attributeConcurrencyLimit; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Variables +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% TRUE, if i-th element gets scheduled on j-th night. +array[1..numElements, 1..maxTime] of var bool: ELEMENT2TIMESLOT; + +% TRUE, if i-th element gets scheduled to j-th loader. +array[1..numElements, 1..numLoaders] of var bool: ELEMENT2LOADER; + +% Indicates the time slot (nigth) in which the elements were scheduled. +array[1..numElements] of var 0..maxTime: SCHEDULED_SLOT = + [sum(j in 1..maxTime)(j * bool2int(ELEMENT2TIMESLOT[i,j])) | i in 1..numElements]; + +% Indicates the loader for which the elements were scheduled. +array[1..numElements] of var 0..numLoaders: SCHEDULED_LOADER = + [sum(l in 1..numLoaders)(l * bool2int(ELEMENT2LOADER[i,l])) | i in 1..numElements]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Constraints +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Required (core) constraints +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Schedule only on noConclict nights. +constraint +forall(i in 1..numElements, j in 1..maxTime)( + ELEMENT2TIMESLOT[i,j] -> noConflict[i,j] +); + +% Schedule a element to a loader +constraint +forall(i in 1..numElements)( + sum(t in 1..maxTime)(bool2int(ELEMENT2TIMESLOT[i,t])) == + sum(l in 1..numLoaders)(bool2int(ELEMENT2LOADER[i,l])) +); + +% Schedule each element exactly one timeslot/night (or none). +constraint +forall(i in 1..numElements)( + sum(j in 1..maxTime)(bool2int(ELEMENT2TIMESLOT[i,j])) <= 1 +); + +% Schedule each element exactly one loader (or none). +constraint +forall(i in 1..numElements)( + sum(j in 1..numLoaders)(bool2int(ELEMENT2LOADER[i,j])) <= 1 +); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Optional constraints (defined via policy) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%These constraints are defined via policy and require sets of parameters also +%defined via policy. They must be both available. + +%%%%%%%%%%%%%%%%%%%% +% General capacity constraints +%%%%%%%%%%%%%%%%%%%% + +% Satisfy element timeslot/nightly capacity. +constraint +forall(j in 1..maxTime)( + sum(i in 1..numElements)(bool2int(ELEMENT2TIMESLOT[i,j])) <= elementSlotCapacity[j] +); + +% Satisfy loader/timeslot capacity. +constraint +forall(l in 1..numLoaders, t in 1..maxTime)( + sum(i in 1..numElements)( + bool2int(ELEMENT2LOADER[i,l] /\ ELEMENT2TIMESLOT[i,t]) + ) <= loaderCapacity[l,t] +); + +%%%%%%%%%%%%%%%%%%%% +% Attribute capacity constraints +%%%%%%%%%%%%%%%%%%%% + +% For attribute a and timeslot/night t, limits the number of elements having +% the same value for attribute k, scheduled in the same timeslot/night t. +constraint +forall(t in 1..maxTime, a in 1..numAttributes)( + forall(m in 1..attributesRange[a])( + sum(i in 1..numElements)( + bool2int(attributes[i,a] == m /\ ELEMENT2TIMESLOT[i,t]) + ) <= attributeConcurrencyLimit[a,t] + ) +); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Objective function +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Computes the number of scheduled elements. +var int: NUM_SCHEDULED = sum(i in 1..numElements)(bool2int(SCHEDULED_SLOT[i] > 0)); + +% Computes the (average) completion time of all elements. Note that average is +% just a simple division by a constant, and it can be dropped from the model +% for robusteness. +var int: TOTAL_COMPLETION_TIME = sum(i in 1..numElements)(SCHEDULED_SLOT[i]); + +% First, maximize the number of scheduled elements (using a heavy weight) +% and then minimize the (average) completion time for all elements. +solve maximize maxTime * numElements * NUM_SCHEDULED - TOTAL_COMPLETION_TIME; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Output +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +output +["results:"] ++ +["\n -"] ++ +["\n num_scheduled: " ++ show(NUM_SCHEDULED)] ++ +["\n total_completion_time: " ++ show(TOTAL_COMPLETION_TIME)] ++ +["\n element_slot_loader: |"] ++ +[ + "\n " ++ show(element) ++ "," ++ show(SCHEDULED_SLOT[element]) ++ "," ++ + show(SCHEDULED_LOADER[element]) +| element in 1..numElements +] + +%output +%["\n - num_scheduled: " ++ show(NUM_SCHEDULED)] ++ +%["\n total_completion_time: " ++ show(TOTAL_COMPLETION_TIME)] ++ +%["\n elementSlotLoader |"] ++ +%[ "\n " ++ show(element) ++ "," ++ +%show(SCHEDULED_SLOT[element]) ++ "," ++ show(SCHEDULED_LOADER[element]) | element in +%1..numElements ] diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java index 84fc039..23e3ad8 100644 --- a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java @@ -211,5 +211,17 @@ public class RecurringWindows { return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS); } + public static DateTimeIterator getRecurringListForChangeWindow(ChangeWindow window, Long durationInSeconds) + throws ParseException { + + String rdata = "RRULE:FREQ=MINUTELY;INTERVAL=" + durationInSeconds/60; + DateTime start = new DateTime(window.getStartTime().toInstant().toEpochMilli()); + DateTimeIterator recur = + DateTimeIteratorFactory.createDateTimeIterator(rdata, start, DateTimeZone.UTC, true); + return recur; + } + + + } diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java new file mode 100644 index 0000000..4766db2 --- /dev/null +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java @@ -0,0 +1,197 @@ +/* + * ============LICENSE_START============================================== + * Copyright (c) 2019 AT&T Intellectual Property. + * ======================================================================= + * Licensed 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. + * ============LICENSE_END================================================= + */ + +package org.onap.optf.cmso.optimizer.clients.optimizer; + +import com.google.ical.compat.jodatime.DateTimeIterator; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import org.joda.time.DateTime; +import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology; +import org.onap.optf.cmso.optimizer.availability.timewindows.RecurringWindows; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerParameters; +import org.onap.optf.cmso.optimizer.clients.ticketmgt.models.ActiveTicketsResponse; +import org.onap.optf.cmso.optimizer.clients.ticketmgt.models.TicketData; +import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyElementInfo; +import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyResponse; +import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow; +import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; +import org.springframework.expression.spel.ast.OpInc; + +public class ElementAvailability extends ElementWindowMapping{ + + private List<TimeLimitAndVerticalTopology> policies; + private ActiveTicketsResponse ticketResponse; + + private OptimizerParameters parameters = null; + + private List<List<ChangeWindow>> globalRelativeAvailability = new ArrayList<>(); + + private Map<String, List<TicketData>> nodeUnAvailability = new TreeMap<>(); + + public ElementAvailability(List<TimeLimitAndVerticalTopology> policies, OptimizerRequest optimizerRequest, + TopologyResponse topologyResponse, ActiveTicketsResponse ticketResponse) throws ParseException + { + super(optimizerRequest, topologyResponse); + this.policies = policies; + this.ticketResponse = ticketResponse; + } + + public void populate(OptimizerParameters parameters) throws ParseException { + this.parameters = parameters; + for (ChangeWindow changeWindow : optimizerRequest.getChangeWindows()) { + if (policies.size() > 0) { + globalRelativeAvailability.add(RecurringWindows.getAvailabilityWindowsForPolicies(policies, changeWindow)); + } + else { + List<ChangeWindow> wholeWindow = new ArrayList<>(); + wholeWindow.add(changeWindow); + globalRelativeAvailability.add(wholeWindow); + } + } + for (String id : nodeInfo.keySet()) { + calculateNodeAvailability(nodeInfo.get(id)); + } + setNoConflicts(); + parameters.setMaxTime(new Long(parameters.getNoConflict().get(0).size())); + parameters.setNumElements(new Long(parameters.getNoConflict().size())); + + // for now we have 1 loader with unlimited capacity + parameters.setNumLoaders(1L); + Long loaderCapacity = parameters.getNumElements(); + List<Long> capacity = new ArrayList<>(); + for (Long slot =0L ; slot < parameters.getMaxTime() ; slot++) { + capacity.add(loaderCapacity); + } + parameters.getLoaderCapacity().add(capacity); + + // For now every slot has the same concurrency limit + capacity = new ArrayList<>(); + Long limit = new Long(optimizerRequest.getConcurrencyLimit()); + if (limit > parameters.getNumElements()) { + limit = parameters.getNumElements(); + } + + for (Long slot =0L ; slot < parameters.getMaxTime() ; slot++) { + capacity.add(limit); + } + parameters.setElementSlotCapacity(capacity); + + } + + private void setNoConflicts() throws ParseException { + // Only support 1 change window for now + ChangeWindow window = optimizerRequest.getChangeWindows().get(0); + Long duration = new Long(optimizerRequest.getNormalDuration()); + if (optimizerRequest.getAdditionalDuration() != null) { + duration += optimizerRequest.getAdditionalDuration(); + } + for (String elementId : nodeInfo.keySet()) { + + TopologyElementInfo info = nodeInfo.get(elementId); + Long timeZoneOffset = getTimeZoneOffset(info); + DateTimeIterator recur = getRecurringIterator(); + List<Boolean> element = new ArrayList<>(); + while (recur.hasNext()) { + DateTime next = recur.next(); + if (next.isAfter(window.getEndTime().getTime())) { + break; + } + ChangeWindow slot = new ChangeWindow(); + slot.setStartTime(next.toDate()); + slot.setEndTime(next.plus(duration).toDate()); + if (slotIsAvailable(slot, timeZoneOffset, nodeUnAvailability.get(elementId))) { + element.add(true); + } else { + element.add(false); + } + } + parameters.getNoConflict().add(element); + } + + } + + private boolean slotIsAvailable(ChangeWindow slot, Long timeZoneOffset, List<TicketData> tickets) { + if (isGloballyAvailable(slot, timeZoneOffset) && isNotRestricted(slot, tickets)) { + return true; + } + return false; + } + + private boolean isNotRestricted(ChangeWindow slot, List<TicketData> tickets) { + if (tickets != null) { + for (TicketData ticket : tickets) { + ChangeWindow window = new ChangeWindow(); + window.setStartTime(ticket.getStartTime()); + window.setEndTime(ticket.getEndTime()); + if (slot.overlaps(window)) { + return false; + } + } + } + return true; + } + + private boolean isGloballyAvailable(ChangeWindow slot, Long timeZoneOffset) { + for (ChangeWindow global : globalRelativeAvailability.get(0)) { + if (global.containsInTimeZone(slot, timeZoneOffset)) { + return true; + } + } + return false; + } + + private Long getTimeZoneOffset(TopologyElementInfo info) { + // TODO Auto-generated method stub + return 0L; + } + + private void calculateNodeAvailability(TopologyElementInfo info) { + Set<String> requiredElements = new HashSet<>(); + requiredElements.add(info.getElementId()); + if (info.getRequiredElements() != null) { + requiredElements.addAll(info.getRequiredElements()); + } + if (ticketResponse.getElements() != null) { + List<TicketData> tickets = ticketResponse.getElements(); + for (TicketData data : tickets) { + for (String id : data.getElementIds()) { + if (requiredElements.contains(id)) { + updateNodeAvailability(id, data); + break; + } + } + } + } + } + + private void updateNodeAvailability(String elementId, TicketData data) { + List<TicketData> list = nodeUnAvailability.get(elementId); + if (list == null) { + list = new ArrayList<>(); + nodeUnAvailability.put(elementId, list); + } + list.add(data); + } + +} diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java new file mode 100644 index 0000000..42c69a2 --- /dev/null +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java @@ -0,0 +1,148 @@ +/* + * ============LICENSE_START============================================== + * Copyright (c) 2019 AT&T Intellectual Property. + * ======================================================================= + * Licensed 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. + * ============LICENSE_END================================================= + */ + +package org.onap.optf.cmso.optimizer.clients.optimizer; + +import com.google.ical.compat.jodatime.DateTimeIterator; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; +import org.joda.time.DateTime; +import org.onap.optf.cmso.optimizer.availability.timewindows.RecurringWindows; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.ElementSlot; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerSchedule; +import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyElementInfo; +import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyResponse; +import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow; +import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; +import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerScheduleInfo; +import org.onap.optf.cmso.optimizer.service.rs.models.ScheduledElement; +import org.onap.optf.cmso.optimizer.service.rs.models.ScheduledElement.ScheduleType; +import org.onap.optf.cmso.optimizer.service.rs.models.UnScheduledElement; +import org.onap.optf.cmso.optimizer.service.rs.models.UnScheduledElement.NotScheduledReason; + +// This class ensures that the node indices nodes and the time slots are the +// same when processing the optimizer engine response as when initiating. +public class ElementWindowMapping { + + protected OptimizerRequest optimizerRequest; + protected TopologyResponse topologyResponse; + + protected Map<String, TopologyElementInfo> nodeInfo = new TreeMap<>(); + private List<TopologyElementInfo> nodeArray = null; + + public ElementWindowMapping(OptimizerRequest optimizerRequest, TopologyResponse topologyResponse) + throws ParseException { + this.optimizerRequest = optimizerRequest; + this.topologyResponse = topologyResponse; + initialize(); + + } + + private void initialize() throws ParseException { + List<TopologyElementInfo> elements = topologyResponse.getElements(); + for (TopologyElementInfo info : elements) { + nodeInfo.put(info.getElementId(), info); + } + } + + protected DateTimeIterator getRecurringIterator() throws ParseException { + // Only support 1 change window for now + ChangeWindow window = optimizerRequest.getChangeWindows().get(0); + Long duration = new Long(optimizerRequest.getNormalDuration()); + if (optimizerRequest.getAdditionalDuration() != null) { + duration += optimizerRequest.getAdditionalDuration(); + } + DateTimeIterator recur = RecurringWindows.getRecurringListForChangeWindow(window, duration); + return recur; + } + + public void initializeForProcessResult() + { + // we need nodeInfo to be an array to speed up the result processing. + // but we need it sorted by elementId as when we created it.... + nodeArray = nodeInfo.values().stream().collect(Collectors.toList()); + nodeInfo.clear(); + + } + public OptimizerScheduleInfo processResult(OptimizerSchedule result) throws ParseException { + // When considering the memory vs performance + // 5 minute duration for a month long change window is 8928 slots + // The assumption is that there were be fewer allocated slots + // than potential slots. + List<ElementSlot> elements = result.getElementSlotLoader(); + Map<Integer, List<ElementSlot>> mapSlotToElement = elements.stream(). + collect(Collectors.groupingBy(ElementSlot::getSlot)); + DateTimeIterator iter = getRecurringIterator(); + // TODO - supporting only 1 change window at the moment..... + Long endWindow = optimizerRequest.getChangeWindows().get(0).getEndTime().getTime(); + Integer slotIndex = 1; + while (iter.hasNext()) { + DateTime dateTime = iter.next(); + if (dateTime.isAfter(endWindow)) + break; + List<ElementSlot> list = mapSlotToElement.get(slotIndex); + if (list != null) { + list.stream().forEach(x -> x.setTime(dateTime.getMillis())); + } + slotIndex++; + } + // + // All assigned ElementSlots now have corresponding UTC time + // + OptimizerScheduleInfo info = new OptimizerScheduleInfo(); + for (ElementSlot slot : elements) + { + updateInfo(slot, info); + } + return info; + } + + private void updateInfo(ElementSlot slot, OptimizerScheduleInfo info) + { + TopologyElementInfo element = nodeArray.get(slot.getElementIndex()-1); + if (slot.getSlot() > 0) + { + ScheduledElement scheduled = new ScheduledElement(); + Integer durationInSeconds = optimizerRequest.getNormalDuration(); + if (optimizerRequest.getAdditionalDuration() != null) { + durationInSeconds += optimizerRequest.getAdditionalDuration(); + } + scheduled.setDurationSeconds(durationInSeconds.longValue()); + scheduled.setElementId(element.getElementId()); + scheduled.setStartTime(new Date(slot.getTime())); + scheduled.setEndTime(new Date(slot.getTime() + (durationInSeconds*1000))); + scheduled.setScheduleType(ScheduleType.INDIVIDUAL); + info.getScheduledElements().add(scheduled); + } + else + { + UnScheduledElement unscheduled = new UnScheduledElement(); + unscheduled.setElementId(element.getElementId()); + unscheduled.setGroupId("unknown"); + unscheduled.getNotScheduledReaons().add(NotScheduledReason.Other); + unscheduled.getNotScheduledMessages().add("Unknown"); + info.getUnScheduledElements().add(unscheduled); + } + } + + +} diff --git a/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java new file mode 100644 index 0000000..514097e --- /dev/null +++ b/cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java @@ -0,0 +1,279 @@ +/* + * ============LICENSE_START============================================== + * Copyright (c) 2019 AT&T Intellectual Property. + * ======================================================================= + * Licensed 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. + * ============LICENSE_END================================================= + * + */ + +package org.onap.optf.cmso.optimizer.clients.optimizer; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import org.apache.commons.io.IOUtils; +import org.onap.observations.Observation; +import org.onap.optf.cmso.common.PropertiesManagement; +import org.onap.optf.cmso.common.exceptions.CmsoException; +import org.onap.optf.cmso.optimizer.availability.policies.PolicyManager; +import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerEngineResponse; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerEngineResponse.OptimizerEngineResponseStatus; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerParameters; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerResponseUtility; +import org.onap.optf.cmso.optimizer.clients.optimizer.models.OptimizerResults; +import org.onap.optf.cmso.optimizer.clients.ticketmgt.TicketMgtRequestManager; +import org.onap.optf.cmso.optimizer.clients.ticketmgt.models.ActiveTicketsResponse; +import org.onap.optf.cmso.optimizer.clients.topology.TopologyRequestManager; +import org.onap.optf.cmso.optimizer.clients.topology.models.TopologyResponse; +import org.onap.optf.cmso.optimizer.common.LogMessages; +import org.onap.optf.cmso.optimizer.model.Optimizer; +import org.onap.optf.cmso.optimizer.model.Request; +import org.onap.optf.cmso.optimizer.model.Ticket; +import org.onap.optf.cmso.optimizer.model.Topology; +import org.onap.optf.cmso.optimizer.model.dao.OptimizerDao; +import org.onap.optf.cmso.optimizer.model.dao.RequestDao; +import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerRequest; +import org.onap.optf.cmso.optimizer.service.rs.models.OptimizerResponse.OptimizeScheduleStatus; +import org.onap.optf.cmso.optimizer.service.rs.models.PolicyInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +/** + * The Class OptimizerClient. + */ +@Component +public class OptimizerClient { + private static EELFLogger debug = EELFManager.getInstance().getDebugLogger(); + + @Autowired + Environment env; + + @Autowired + PropertiesManagement pm; + + @Autowired + RequestDao requestDao; + + @Autowired + TopologyRequestManager topologyRequestManager; + + @Autowired + TicketMgtRequestManager ticketMgtRequestManager; + + @Autowired + OptimizerDao optimizerDao; + + @Autowired + PolicyManager policyManager; + + /** + * Make request. + * + * @param request the request + * @param optimizer the Optimizer + * @return the Optimizer response + */ + public OptimizerEngineResponse makeRequest(Request request, Optimizer optimizer) { + Integer maxAttempts = env.getProperty("cmso.optimizer.maxAttempts", Integer.class, 20); + OptimizerEngineResponse apiResponse = new OptimizerEngineResponse(); + if (optimizer.getOptimizeRetries() >= maxAttempts) { + apiResponse.setStatus(OptimizerEngineResponseStatus.FAILED); + apiResponse.setErrorMessage(LogMessages.EXCEEDED_RETRY_LIMIT.format("Optimizer", maxAttempts.toString())); + Observation.report(LogMessages.EXCEEDED_RETRY_LIMIT, "Optimizer", maxAttempts.toString()); + return apiResponse; + } + OptimizerRequest optimizerRequest = null; + TopologyResponse topologyResponse = null; + ActiveTicketsResponse ticketResponse = null; + try { + optimizerRequest = getOptimizerRequest(request); + topologyResponse = getTopologyResponse(request.getUuid()); + ticketResponse = getTicketResponse(request.getUuid()); + OptimizerParameters optimizerParameters = + buildOptimizerParameters(optimizerRequest, topologyResponse, ticketResponse); + apiResponse = initiateOptimizer(optimizerParameters, request); + } catch (Exception e) { + apiResponse.setStatus(OptimizerEngineResponseStatus.FAILED); + apiResponse.setErrorMessage(LogMessages.UNEXPECTED_EXCEPTION.format(e.getMessage())); + Observation.report(LogMessages.UNEXPECTED_EXCEPTION, e, e.getMessage()); + } + return apiResponse; + } + + private OptimizerParameters buildOptimizerParameters(OptimizerRequest optimizerRequest, + TopologyResponse topologyResponse, ActiveTicketsResponse ticketResponse) throws ParseException { + List<TimeLimitAndVerticalTopology> policies = getPolicies(optimizerRequest); + OptimizerParameters parameters = new OptimizerParameters(); + ElementAvailability elementAvailability = + new ElementAvailability(policies, optimizerRequest, topologyResponse, ticketResponse); + elementAvailability.populate(parameters); + + // Policies for this are undefined... + parameters.setAttributes(getAttributes(policies, optimizerRequest)); + parameters.setAttributesRange(getAttributesRange(policies, optimizerRequest)); + parameters.setAttributeConcurrencyLimit(getAttributeConcrrencyLimit(policies, optimizerRequest)); + parameters.setNumAttributes(new Long(parameters.getAttributesRange().size())); + return parameters; + } + + private List<List<Long>> getAttributeConcrrencyLimit(List<TimeLimitAndVerticalTopology> policies, + OptimizerRequest optimizerRequest) { + List<List<Long>> list = new ArrayList<>(); + return list; + } + + private List<Long> getAttributesRange(List<TimeLimitAndVerticalTopology> policies, + OptimizerRequest optimizerRequest) { + List<Long> list = new ArrayList<>(); + return list; + } + + private List<List<Long>> getAttributes(List<TimeLimitAndVerticalTopology> policies, + OptimizerRequest optimizerRequest) { + List<List<Long>> list = new ArrayList<>(); + return list; + } + + + private List<TimeLimitAndVerticalTopology> getPolicies(OptimizerRequest optimizerRequest) { + List<TimeLimitAndVerticalTopology> list = new ArrayList<>(); + for (PolicyInfo policyInfo : optimizerRequest.getPolicies()) { + TimeLimitAndVerticalTopology policy = + policyManager.getTimeLimitAndVerticalTopologyByName(policyInfo.getPolicyName()); + list.add(policy); + } + return list; + } + + private ActiveTicketsResponse getTicketResponse(UUID uuid) + throws JsonParseException, JsonMappingException, IOException { + Ticket ticketRow = ticketMgtRequestManager.getExistingTickets(uuid); + String ticketString = ticketRow.getTickets(); + ObjectMapper om = new ObjectMapper(); + return om.readValue(ticketString, ActiveTicketsResponse.class); + } + + private TopologyResponse getTopologyResponse(UUID uuid) + throws JsonParseException, JsonMappingException, IOException { + Topology topologyRow = topologyRequestManager.getExistingTopology(uuid); + String topologyString = topologyRow.getTopology(); + ObjectMapper om = new ObjectMapper(); + return om.readValue(topologyString, TopologyResponse.class); + } + + private OptimizerRequest getOptimizerRequest(Request request) + throws JsonParseException, JsonMappingException, IOException { + String requestString = request.getRequest(); + ObjectMapper om = new ObjectMapper(); + return om.readValue(requestString, OptimizerRequest.class); + } + + + private OptimizerEngineResponse initiateOptimizer(OptimizerParameters request, Request requestRow) + throws CmsoException, JsonProcessingException { + + + UUID uuid = requestRow.getUuid(); + OptimizerEngineResponse apiResponse = new OptimizerEngineResponse(); + apiResponse.setRequestId(uuid.toString()); + + String workingFolderString = env.getProperty("cmso.optimizer.engine.working.folder", "data/engine"); + File workingFolder = new File(workingFolderString + File.separator + requestRow.getUuid().toString()); + workingFolder.mkdirs(); + Long timeLimit = env.getProperty("cmso.minizinc.command.timelimit", Long.class); + // TODO calculate time limit + Process p = null; + try { + Path inputFileName = Paths.get(workingFolder.getAbsolutePath(), "input.dzn"); + Path outputFileName = Paths.get(workingFolder.getAbsolutePath(), "results.yaml"); + String dzn = request.toMiniZinc(); + Files.write(inputFileName, dzn.getBytes()); + List<String> command = buildCommand(inputFileName, outputFileName, timeLimit.toString()); + debug.debug("engine command=", command.toString()); + ProcessBuilder pb = new ProcessBuilder(command); + p = pb.start(); + String stdout = IOUtils.toString(p.getInputStream(), "UTF-8"); + String stderr = IOUtils.toString(p.getErrorStream(), "UTF-8"); + debug.debug("stdout=" + stdout); + debug.debug("stderr=" + stderr); + if (p.isAlive()) { + p.wait(); + } + OptimizerResponseUtility responseUtility = new OptimizerResponseUtility(); + OptimizerResults optimizerResults = responseUtility.parseOptimizerResult(outputFileName.toFile()); + apiResponse.setOptimizerResults(optimizerResults); + apiResponse.setStatus(OptimizerEngineResponseStatus.COMPLETED); + + } catch (InterruptedException e) { + apiResponse.setStatus(OptimizerEngineResponseStatus.FAILED); + apiResponse.setErrorMessage( + LogMessages.OPTIMIZER_REQUEST_TIMEOUT.format(uuid.toString(), timeLimit.toString())); + Observation.report(LogMessages.OPTIMIZER_REQUEST_TIMEOUT, uuid.toString(), timeLimit.toString()); + p.destroyForcibly(); + } catch (Exception e) { + apiResponse.setStatus(OptimizerEngineResponseStatus.FAILED); + apiResponse.setErrorMessage(LogMessages.UNEXPECTED_EXCEPTION.format(e.getMessage())); + Observation.report(LogMessages.UNEXPECTED_RESPONSE, e, e.getMessage()); + } finally { + if (workingFolder.exists()) { + workingFolder.delete(); + } + } + return apiResponse; + } + + private List<String> buildCommand(Path inputFileName, Path outputFileName, String timeLimit) { + List<String> command = new ArrayList<>(); + String minizinc = env.getProperty("cmso.minizinc.command.exe", "minizinc"); + String solver = env.getProperty("cmso.minizinc.command.solver", "OSICBC"); + String additional = env.getProperty("cmso.minizinc.command.additional", ""); + String script = env.getProperty("cmso.minizinc.command.mzn", "scripts/minizinc/generic_attributes.mzn"); + + command.add(minizinc); + command.add("--solver"); + command.add(solver); + command.add("--time-limit"); + command.add(timeLimit); + command.add("--time-limit"); + command.add(timeLimit); + command.add("--soln-sep"); + command.add("\"\""); + command.add("--search-complete-msg"); + command.add("\"\""); + for (String add : additional.split(" ")) { + command.add(add); + } + command.add("-o"); + command.add(outputFileName.toString()); + command.add(script); + command.add(inputFileName.toString()); + return command; + } + + +} |