b3829c30b7f2ce0432cb825b768e77953f43365e
[optf/cmso.git] /
1 /*
2  * ============LICENSE_START==============================================
3  * Copyright (c) 2019 AT&T Intellectual Property.
4  * =======================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License"); you may
6  * not use this file except in compliance with the License. You may obtain a
7  * copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing
15  * permissions and limitations under the License.
16  * ============LICENSE_END=================================================
17  *
18  */
19
20 package org.onap.optf.cmso.optimizer.availability.timewindows;
21
22 import com.google.ical.compat.jodatime.DateTimeIterator;
23 import com.google.ical.compat.jodatime.DateTimeIteratorFactory;
24 import java.text.ParseException;
25 import java.time.Instant;
26 import java.time.LocalDate;
27 import java.time.LocalDateTime;
28 import java.time.LocalTime;
29 import java.time.OffsetDateTime;
30 import java.time.OffsetTime;
31 import java.time.ZoneOffset;
32 import java.time.ZonedDateTime;
33 import java.time.temporal.ChronoUnit;
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 import org.joda.time.DateTime;
39 import org.joda.time.DateTimeZone;
40 import org.onap.optf.cmso.optimizer.availability.policies.model.AllowedPeriodicTime;
41 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology;
42 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeRange;
43 import org.onap.optf.cmso.optimizer.common.LogMessages;
44 import org.onap.optf.cmso.optimizer.observations.Observation;
45 import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow;
46
47 /**
48  * The Class RecurringWindows.
49  */
50 public class RecurringWindows {
51
52     /**
53      * Gets the availability windows for policies.
54      *
55      * @param policies the policies
56      * @param changeWindow the change window
57      * @return the availability windows for policies
58      */
59     public static List<ChangeWindow> getAvailabilityWindowsForPolicies(List<TimeLimitAndVerticalTopology> policies,
60                     ChangeWindow changeWindow) {
61         List<ChangeWindow> availableList = new ArrayList<>();
62         for (TimeLimitAndVerticalTopology policy : policies) {
63             if (policy.getTimeSchedule() != null && policy.getTimeSchedule().getAllowedPeriodicTime() != null) {
64                 for (AllowedPeriodicTime available : policy.getTimeSchedule().getAllowedPeriodicTime()) {
65                     getAvailableWindowsForApt(available, changeWindow, availableList);
66                 }
67             }
68         }
69         // Collapse all duplicate and overlapping availabity windows into minimum
70         // number of windows
71         availableList = collapseWindows(availableList);
72         return availableList;
73
74     }
75
76
77     private static List<ChangeWindow> collapseWindows(List<ChangeWindow> availableList) {
78         List<ChangeWindow> collapsed = new ArrayList<>();
79         Set<ChangeWindow> consumed = new HashSet<>();
80         for (ChangeWindow win : availableList) {
81             if (!consumed.contains(win)) {
82                 // Find all windows that can collapse into this one
83                 consumed.add(win);
84                 boolean allUnique = false;
85                 while (!allUnique) {
86                     allUnique = true;
87                     for (ChangeWindow test : availableList) {
88                         // if availability windows overlap
89                         if (!consumed.contains(test)) {
90                             if (win.absorbIfOverlapping(test)) {
91                                 consumed.add(test);
92                                 allUnique = false;
93                             }
94                         }
95                     }
96                 }
97                 collapsed.add(win);
98             }
99         }
100         return collapsed;
101     }
102
103     // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
104     private static void getAvailableWindowsForApt(AllowedPeriodicTime available, ChangeWindow changeWindow,
105                     List<ChangeWindow> availableList) {
106
107         if (available.getDay() != null) {
108             switch (available.getDay()) {
109                 case weekday:
110                 case weekend:
111                     getAvailableWindowsForAptDay(available, changeWindow, availableList);
112                     return;
113                 default:
114
115             }
116         }
117         availableList.add(changeWindow);
118         Observation.report(LogMessages.UNSUPPORTED_PERIODIC_TIME, available.toString());
119
120     }
121
122     // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
123
124     private static void getAvailableWindowsForAptDay(AllowedPeriodicTime available, ChangeWindow changeWindow,
125                     List<ChangeWindow> availableList) {
126         try {
127             List<TimeRange> ranges = available.getTimeRange();
128             if (ranges.size() == 0) {
129                 TimeRange range = new TimeRange();
130                 range.setStartTime("00:00:00+00:00");
131                 range.setEndTime("24:00:00+00:00");
132                 ranges.add(range);
133             }
134             StringBuilder rdata = new StringBuilder();
135             rdata.append(available.getDay().getRrule()).append("\n");
136             for (TimeRange range : ranges) {
137                 processRange(range, changeWindow, availableList, rdata);
138             }
139         } catch (Exception e) {
140             Observation.report(LogMessages.UNEXPECTED_EXCEPTION, e, e.getMessage());
141         }
142     }
143
144
145     private static void processRange(TimeRange range, ChangeWindow changeWindow, List<ChangeWindow> availableList,
146                     StringBuilder rdata) throws ParseException {
147
148         Instant cwStartInstant = changeWindow.getStartTime().toInstant();
149         Instant cwEndInstant = changeWindow.getEndTime().toInstant();
150
151         List<DateTime> startList = getRecurringList(range.getStartTime(), cwStartInstant, rdata, cwEndInstant, -1);
152         List<DateTime> endList = getRecurringList(range.getEndTime(), cwStartInstant,
153                         rdata, cwEndInstant, startList.size());
154         // Pair them up to make change windows
155         // Everything should be UTC time
156         for (int i = 0; i < startList.size(); i++) {
157             DateTime startDt = startList.get(i);
158             if (i < endList.size()) {
159                 DateTime endDt = endList.get(i);
160                 if (endDt.isAfter(startDt)) {
161                     ChangeWindow cw = new ChangeWindow();
162                     cw.setStartTime(startDt.toDate());
163                     cw.setEndTime(endDt.toDate());
164                     availableList.add(cw);
165                 }
166
167             }
168         }
169
170     }
171
172
173     private static List<DateTime> getRecurringList(String rangeTime, Instant cwStartInstant, StringBuilder rdata,
174                     Instant cwEndInstant, int endingSize) throws ParseException {
175
176         Instant startInstant = getInstanceFromTime(rangeTime, cwStartInstant);
177         DateTime start = new DateTime(startInstant.toEpochMilli());
178         DateTimeIterator recur =
179                         DateTimeIteratorFactory.createDateTimeIterator(rdata.toString(), start, DateTimeZone.UTC, true);
180         List<DateTime> list = new ArrayList<>();
181         while (recur.hasNext()) {
182             DateTime next = recur.next();
183             // System.out.println(next.toString());
184             if (endingSize == -1) {
185                 if (next.isAfter(cwEndInstant.toEpochMilli())) {
186                     break;
187                 }
188             }
189             else {
190                 if  (list.size() == endingSize) {
191                     break;
192                 }
193             }
194             list.add(next);
195         }
196         return list;
197     }
198
199
200     //
201     // The policies with 'Day' enumeration only have time with no day so we add the
202     // date portion of the change window to the dtstart
203     //
204     private static Instant getInstanceFromTime(String timeIn, Instant cwStartInstant) {
205         Instant instant = null;
206         Instant date = cwStartInstant.truncatedTo(ChronoUnit.DAYS);
207         // Handle ending midnight
208         if (timeIn.startsWith("24:00:00")) {
209             timeIn = "00:00:00+00:00";
210             date = date.plus(1, ChronoUnit.DAYS);
211         }
212         LocalDate epoch = LocalDate.ofEpochDay(0);
213         try {
214             OffsetTime offset = OffsetTime.parse(timeIn);
215             OffsetDateTime odt = offset.atDate(epoch);
216             ZonedDateTime startTime = odt.atZoneSameInstant(ZoneOffset.UTC.normalized());
217             instant = Instant.from(startTime);
218         } catch (Exception e) {
219             LocalTime local = LocalTime.parse(timeIn);
220             LocalDateTime ldt = local.atDate(epoch);
221             ZonedDateTime startTime = ldt.atZone(ZoneOffset.UTC.normalized());
222             instant = Instant.from(startTime);
223         }
224         return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS);
225     }
226
227     /**
228      * Gets the recurring list for change window.
229      *
230      * @param window the window
231      * @param durationInSeconds the duration in seconds
232      * @return the recurring list for change window
233      * @throws ParseException the parse exception
234      */
235     public static DateTimeIterator getRecurringListForChangeWindow(ChangeWindow window, Long durationInSeconds)
236                     throws ParseException {
237
238         String rdata = "RRULE:FREQ=MINUTELY;INTERVAL=" + durationInSeconds / 60;
239         DateTime start = new DateTime(window.getStartTime().toInstant().toEpochMilli());
240         DateTimeIterator recur =
241                         DateTimeIteratorFactory.createDateTimeIterator(rdata, start, DateTimeZone.UTC, true);
242         return recur;
243     }
244
245
246
247
248 }