dce64b469c1e107eb84dca79a7d963eb48d60455
[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.Date;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39 import org.joda.time.DateTime;
40 import org.joda.time.DateTimeZone;
41 import org.onap.observations.Observation;
42 import org.onap.optf.cmso.optimizer.availability.policies.model.AllowedPeriodicTime;
43 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology;
44 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeRange;
45 import org.onap.optf.cmso.optimizer.common.LogMessages;
46 import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow;
47
48 /**
49  * The Class RecurringWindows.
50  */
51 public class RecurringWindows {
52
53     /**
54      * Gets the availability windows for policies.
55      *
56      * @param policies the policies
57      * @param changeWindow the change window
58      * @return the availability windows for policies
59      */
60     public static List<ChangeWindow> getAvailabilityWindowsForPolicies(List<TimeLimitAndVerticalTopology> policies,
61                     ChangeWindow changeWindow) {
62         List<ChangeWindow> availableList = new ArrayList<>();
63         for (TimeLimitAndVerticalTopology policy : policies) {
64             if (policy.getTimeSchedule() != null && policy.getTimeSchedule().getAllowedPeriodicTime() != null) {
65                 for (AllowedPeriodicTime available : policy.getTimeSchedule().getAllowedPeriodicTime()) {
66                     getAvailableWindowsForApt(available, changeWindow, availableList);
67                 }
68             }
69         }
70         // Collapse all duplicate and overlapping availabity windows into minimum
71         // number of windows
72         availableList = collapseWindows(availableList);
73         return availableList;
74
75     }
76
77
78     private static List<ChangeWindow> collapseWindows(List<ChangeWindow> availableList) {
79         List<ChangeWindow> collapsed = new ArrayList<>();
80         Set<ChangeWindow> consumed = new HashSet<>();
81         for (ChangeWindow win : availableList) {
82             if (!consumed.contains(win)) {
83                 // Find all windows that can collapse into this one
84                 consumed.add(win);
85                 boolean allUnique = false;
86                 while (!allUnique) {
87                     allUnique = true;
88                     for (ChangeWindow test : availableList) {
89                         // if availability windows overlap
90                         if (!consumed.contains(test)) {
91                             if (win.absorbIfOverlapping(test)) {
92                                 consumed.add(test);
93                                 allUnique = false;
94                             }
95                         }
96                     }
97                 }
98                 collapsed.add(win);
99             }
100         }
101         return collapsed;
102     }
103
104     // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
105     private static void getAvailableWindowsForApt(AllowedPeriodicTime available, ChangeWindow changeWindow,
106                     List<ChangeWindow> availableList) {
107
108         if (available.getDay() != null) {
109             switch (available.getDay()) {
110                 case weekday:
111                 case weekend:
112                     getAvailableWindowsForAptDay(available, changeWindow, availableList);
113                     return;
114                 default:
115
116             }
117         }
118         availableList.add(changeWindow);
119         Observation.report(LogMessages.UNSUPPORTED_PERIODIC_TIME, available.toString());
120
121     }
122
123     // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
124
125     private static void getAvailableWindowsForAptDay(AllowedPeriodicTime available, ChangeWindow changeWindow,
126                     List<ChangeWindow> availableList) {
127         try {
128             List<TimeRange> ranges = available.getTimeRange();
129             if (ranges.size() == 0) {
130                 TimeRange range = new TimeRange();
131                 range.setStart_time("00:00:00+00:00");
132                 range.setStart_time("23:59:59+00:00");
133                 ranges.add(range);
134             }
135             StringBuilder rdata = new StringBuilder();
136             rdata.append(available.getDay().getRrule()).append("\n");
137             for (TimeRange range : ranges) {
138                 processRange(range, changeWindow, availableList, rdata);
139             }
140         } catch (Exception e) {
141             Observation.report(LogMessages.UNEXPECTED_EXCEPTION, e, e.getMessage());
142         }
143     }
144
145
146     private static void processRange(TimeRange range, ChangeWindow changeWindow, List<ChangeWindow> availableList,
147                     StringBuilder rdata) throws ParseException {
148
149         Instant cwStartInstant = changeWindow.getStartTime().toInstant();
150         Instant cwEndInstant = changeWindow.getEndTime().toInstant();
151
152         List<DateTime> startList = getRecurringList(range.getStart_time(), cwStartInstant, rdata, cwEndInstant);
153         List<DateTime> endList = getRecurringList(range.getEnd_time(), cwStartInstant, rdata, cwEndInstant);
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) 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 (next.isAfter(cwEndInstant.toEpochMilli())) {
185                 break;
186             }
187             list.add(next);
188         }
189         return list;
190     }
191
192
193     //
194     // The policies with 'Day' enumeration only have time with no day so we add the
195     // date portion of the change window to the dtstart
196     //
197     private static Instant getInstanceFromTime(String timeIn, Instant cwStartInstant) {
198         Instant instant = null;
199         Instant date = cwStartInstant.truncatedTo(ChronoUnit.DAYS);
200         LocalDate epoch = LocalDate.ofEpochDay(0);
201         try {
202             OffsetTime offset = OffsetTime.parse(timeIn);
203             OffsetDateTime odt = offset.atDate(epoch);
204             ZonedDateTime startTime = odt.atZoneSameInstant(ZoneOffset.UTC.normalized());
205             instant = Instant.from(startTime);
206         } catch (Exception e) {
207             LocalTime local = LocalTime.parse(timeIn);
208             LocalDateTime ldt = local.atDate(epoch);
209             ZonedDateTime startTime = ldt.atZone(ZoneOffset.UTC.normalized());
210             instant = Instant.from(startTime);
211         }
212         return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS);
213     }
214
215
216 }