aboutsummaryrefslogtreecommitdiffstats
path: root/cmso-optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'cmso-optimizer')
-rw-r--r--cmso-optimizer/etc/config/optimizer.properties7
-rw-r--r--cmso-optimizer/pom.xml5
-rw-r--r--cmso-optimizer/scripts/minizinc/generic_attributes.mzn199
-rw-r--r--cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/availability/timewindows/RecurringWindows.java12
-rw-r--r--cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementAvailability.java197
-rw-r--r--cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/ElementWindowMapping.java148
-rw-r--r--cmso-optimizer/src/main/java/org/onap/optf/cmso/optimizer/clients/optimizer/OptimizerClient.java279
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;
+ }
+
+
+}