c3536b79927acfeb0cc92c783914abe54acd5b55
[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.observations.Observation;
41 import org.onap.optf.cmso.optimizer.availability.policies.model.AllowedPeriodicTime;
42 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology;
43 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeRange;
44 import org.onap.optf.cmso.optimizer.common.LogMessages;
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.setStart_time("00:00:00+00:00");
131                 range.setStart_time("23:59:59+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.getStart_time(), cwStartInstant, rdata, cwEndInstant, -1);
152         List<DateTime> endList = getRecurringList(range.getEnd_time(), cwStartInstant, rdata, cwEndInstant, startList.size());
153         // Pair them up to make change windows
154         // Everything should be UTC time
155         for (int i = 0; i < startList.size(); i++) {
156             DateTime startDt = startList.get(i);
157             if (i < endList.size()) {
158                 DateTime endDt = endList.get(i);
159                 if (endDt.isAfter(startDt)) {
160                     ChangeWindow cw = new ChangeWindow();
161                     cw.setStartTime(startDt.toDate());
162                     cw.setEndTime(endDt.toDate());
163                     availableList.add(cw);
164                 }
165
166             }
167         }
168
169     }
170
171
172     private static List<DateTime> getRecurringList(String rangeTime, Instant cwStartInstant, StringBuilder rdata,
173                     Instant cwEndInstant, int endingSize) throws ParseException {
174
175         Instant startInstant = getInstanceFromTime(rangeTime, cwStartInstant);
176         DateTime start = new DateTime(startInstant.toEpochMilli());
177         DateTimeIterator recur =
178                         DateTimeIteratorFactory.createDateTimeIterator(rdata.toString(), start, DateTimeZone.UTC, true);
179         List<DateTime> list = new ArrayList<>();
180         while (recur.hasNext()) {
181             DateTime next = recur.next();
182             // System.out.println(next.toString());
183             if (endingSize == -1) {
184             if (next.isAfter(cwEndInstant.toEpochMilli())) {
185                 break;
186                 }
187             }
188             else {
189                 if  (list.size() == endingSize) {
190                     break;
191                 }
192             }
193             list.add(next);
194         }
195         return list;
196     }
197
198
199     //
200     // The policies with 'Day' enumeration only have time with no day so we add the
201     // date portion of the change window to the dtstart
202     //
203     private static Instant getInstanceFromTime(String timeIn, Instant cwStartInstant) {
204         Instant instant = null;
205         Instant date = cwStartInstant.truncatedTo(ChronoUnit.DAYS);
206         LocalDate epoch = LocalDate.ofEpochDay(0);
207         try {
208             OffsetTime offset = OffsetTime.parse(timeIn);
209             OffsetDateTime odt = offset.atDate(epoch);
210             ZonedDateTime startTime = odt.atZoneSameInstant(ZoneOffset.UTC.normalized());
211             instant = Instant.from(startTime);
212         } catch (Exception e) {
213             LocalTime local = LocalTime.parse(timeIn);
214             LocalDateTime ldt = local.atDate(epoch);
215             ZonedDateTime startTime = ldt.atZone(ZoneOffset.UTC.normalized());
216             instant = Instant.from(startTime);
217         }
218         return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS);
219     }
220
221     public static DateTimeIterator getRecurringListForChangeWindow(ChangeWindow window, Long durationInSeconds)
222                     throws ParseException {
223
224         String rdata = "RRULE:FREQ=MINUTELY;INTERVAL=" + durationInSeconds/60;
225         DateTime start = new DateTime(window.getStartTime().toInstant().toEpochMilli());
226         DateTimeIterator recur =
227                         DateTimeIteratorFactory.createDateTimeIterator(rdata, start, DateTimeZone.UTC, true);
228         return recur;
229     }
230
231
232
233
234 }