blob: f11cc05ed3825c22c6862a871d204fc488042252 [file] [log] [blame]
Thomas Vachuska96d55b12015-05-11 08:52:03 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska96d55b12015-05-11 08:52:03 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.rest.resources;
17
Jonathan Hartb11c4d02016-03-23 09:05:44 -070018import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import org.onosproject.net.config.Config;
21import org.onosproject.net.config.NetworkConfigService;
22import org.onosproject.net.config.SubjectFactory;
23import org.onosproject.rest.AbstractWebResource;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070024
25import javax.ws.rs.Consumes;
26import javax.ws.rs.DELETE;
27import javax.ws.rs.GET;
28import javax.ws.rs.POST;
29import javax.ws.rs.Path;
30import javax.ws.rs.PathParam;
31import javax.ws.rs.Produces;
32import javax.ws.rs.core.MediaType;
33import javax.ws.rs.core.Response;
Jonathan Hartb11c4d02016-03-23 09:05:44 -070034import java.io.IOException;
35import java.io.InputStream;
36import java.util.Set;
Ray Milkey36992c82015-11-17 13:31:15 -080037
38import static org.onlab.util.Tools.emptyIsNotFound;
39import static org.onlab.util.Tools.nullIsNotFound;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070040
41/**
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070042 * Manage network configurations.
Thomas Vachuska96d55b12015-05-11 08:52:03 -070043 */
44@Path("network/configuration")
45public class NetworkConfigWebResource extends AbstractWebResource {
46
Ray Milkey36992c82015-11-17 13:31:15 -080047 private String subjectClassNotFoundErrorString(String subjectClassKey) {
48 return "Config for '" + subjectClassKey + "' not found";
49 }
50
51 private String subjectNotFoundErrorString(String subjectClassKey,
52 String subjectKey) {
53 return "Config for '"
54 + subjectClassKey + "/" + subjectKey
55 + "' not found";
56 }
57
58 private String configKeyNotFoundErrorString(String subjectClassKey,
59 String subjectKey,
60 String configKey) {
61 return "Config for '"
62 + subjectClassKey + "/" + subjectKey + "/" + configKey
63 + "' not found";
64 }
65
Thomas Vachuska96d55b12015-05-11 08:52:03 -070066 /**
Jian Licc730a62016-05-10 16:36:16 -070067 * Gets entire network configuration base.
Ray Milkey36992c82015-11-17 13:31:15 -080068 *
Jian Licc730a62016-05-10 16:36:16 -070069 * @return 200 OK with network configuration JSON
Thomas Vachuska96d55b12015-05-11 08:52:03 -070070 */
71 @GET
72 @Produces(MediaType.APPLICATION_JSON)
73 @SuppressWarnings("unchecked")
74 public Response download() {
75 NetworkConfigService service = get(NetworkConfigService.class);
76 ObjectNode root = mapper().createObjectNode();
Thomas Vachuskaea5adc62015-10-07 11:52:30 -070077 service.getSubjectClasses().forEach(sc -> {
78 SubjectFactory subjectFactory = service.getSubjectFactory(sc);
79 produceJson(service, newObject(root, subjectFactory.subjectClassKey()),
80 subjectFactory, sc);
81 });
Thomas Vachuska96d55b12015-05-11 08:52:03 -070082 return ok(root).build();
83 }
84
85 /**
Jian Licc730a62016-05-10 16:36:16 -070086 * Gets all network configuration for a subject class.
Thomas Vachuska96d55b12015-05-11 08:52:03 -070087 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -070088 * @param subjectClassKey subject class key
Jian Licc730a62016-05-10 16:36:16 -070089 * @return 200 OK with network configuration JSON
Thomas Vachuska96d55b12015-05-11 08:52:03 -070090 */
91 @GET
Thomas Vachuskaea5adc62015-10-07 11:52:30 -070092 @Path("{subjectClassKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -070093 @Produces(MediaType.APPLICATION_JSON)
94 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -070095 public Response download(@PathParam("subjectClassKey") String subjectClassKey) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -070096 NetworkConfigService service = get(NetworkConfigService.class);
97 ObjectNode root = mapper().createObjectNode();
Ray Milkey36992c82015-11-17 13:31:15 -080098 SubjectFactory subjectFactory =
99 nullIsNotFound(service.getSubjectFactory(subjectClassKey),
100 subjectClassNotFoundErrorString(subjectClassKey));
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700101 produceJson(service, root, subjectFactory, subjectFactory.subjectClass());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700102 return ok(root).build();
103 }
104
105 /**
Jian Licc730a62016-05-10 16:36:16 -0700106 * Gets all network configuration for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700107 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700108 * @param subjectClassKey subjectKey class key
109 * @param subjectKey subjectKey key
Jian Licc730a62016-05-10 16:36:16 -0700110 * @return 200 OK with network configuration JSON
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700111 */
112 @GET
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700113 @Path("{subjectClassKey}/{subjectKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700114 @Produces(MediaType.APPLICATION_JSON)
115 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700116 public Response download(@PathParam("subjectClassKey") String subjectClassKey,
117 @PathParam("subjectKey") String subjectKey) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700118 NetworkConfigService service = get(NetworkConfigService.class);
119 ObjectNode root = mapper().createObjectNode();
Ray Milkey36992c82015-11-17 13:31:15 -0800120 SubjectFactory subjectFactory =
121 nullIsNotFound(service.getSubjectFactory(subjectClassKey),
122 subjectClassNotFoundErrorString(subjectClassKey));
123 produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey),
124 true,
125 subjectNotFoundErrorString(subjectClassKey, subjectKey));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700126 return ok(root).build();
127 }
128
129 /**
Jian Licc730a62016-05-10 16:36:16 -0700130 * Gets specific network configuration for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700131 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700132 * @param subjectClassKey subjectKey class key
133 * @param subjectKey subjectKey key
134 * @param configKey configuration class key
Jian Licc730a62016-05-10 16:36:16 -0700135 * @return 200 OK with network configuration JSON
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700136 */
137 @GET
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700138 @Path("{subjectClassKey}/{subjectKey}/{configKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700139 @Produces(MediaType.APPLICATION_JSON)
140 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700141 public Response download(@PathParam("subjectClassKey") String subjectClassKey,
142 @PathParam("subjectKey") String subjectKey,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700143 @PathParam("configKey") String configKey) {
144 NetworkConfigService service = get(NetworkConfigService.class);
Ray Milkey36992c82015-11-17 13:31:15 -0800145
146 Object subject =
147 nullIsNotFound(service.getSubjectFactory(subjectClassKey)
148 .createSubject(subjectKey),
149 subjectNotFoundErrorString(subjectClassKey, subjectKey));
150
151 Class configClass =
152 nullIsNotFound(service.getConfigClass(subjectClassKey, configKey),
153 configKeyNotFoundErrorString(subjectClassKey, subjectKey, configKey));
154 Config config =
Jian Lia7cd9d72016-05-10 12:49:40 -0700155 (Config) nullIsNotFound(service.getConfig(subject, configClass),
Ray Milkey36992c82015-11-17 13:31:15 -0800156 configKeyNotFoundErrorString(subjectClassKey,
157 subjectKey,
158 configKey));
159 return ok(config.node()).build();
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700160 }
161
162 @SuppressWarnings("unchecked")
163 private void produceJson(NetworkConfigService service, ObjectNode node,
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700164 SubjectFactory subjectFactory, Class subjectClass) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700165 service.getSubjects(subjectClass).forEach(s ->
Ray Milkey36992c82015-11-17 13:31:15 -0800166 produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s, false, ""));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700167 }
168
169 private void produceSubjectJson(NetworkConfigService service, ObjectNode node,
Ray Milkey36992c82015-11-17 13:31:15 -0800170 Object subject,
171 boolean emptyIsError,
172 String emptyErrorMessage) {
173 Set<? extends Config<Object>> configs = service.getConfigs(subject);
174 if (emptyIsError) {
175 // caller wants an empty set to be a 404
176 configs = emptyIsNotFound(configs, emptyErrorMessage);
177 }
178 configs.forEach(c -> node.set(c.key(), c.node()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700179 }
180
181
182 /**
Jian Licc730a62016-05-10 16:36:16 -0700183 * Uploads bulk network configuration.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700184 *
185 * @param request network configuration JSON rooted at the top node
Jian Licc730a62016-05-10 16:36:16 -0700186 * @return 200 OK
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700187 * @throws IOException if unable to parse the request
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700188 */
189 @POST
190 @Consumes(MediaType.APPLICATION_JSON)
191 @SuppressWarnings("unchecked")
192 public Response upload(InputStream request) throws IOException {
193 NetworkConfigService service = get(NetworkConfigService.class);
194 ObjectNode root = (ObjectNode) mapper().readTree(request);
195 root.fieldNames()
196 .forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700197 service.getSubjectFactory(sk)));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700198 return Response.ok().build();
199 }
200
201 /**
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700202 * Upload multiple network configurations for a subject class.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700203 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700204 * @param subjectClassKey subject class key
205 * @param request network configuration JSON rooted at the top node
Jian Licc730a62016-05-10 16:36:16 -0700206 * @return 200 OK
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700207 * @throws IOException if unable to parse the request
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700208 */
209 @POST
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700210 @Path("{subjectClassKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700211 @Consumes(MediaType.APPLICATION_JSON)
212 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700213 public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700214 InputStream request) throws IOException {
215 NetworkConfigService service = get(NetworkConfigService.class);
216 ObjectNode root = (ObjectNode) mapper().readTree(request);
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700217 consumeJson(service, root, service.getSubjectFactory(subjectClassKey));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700218 return Response.ok().build();
219 }
220
221 /**
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700222 * Upload mutliple network configurations for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700223 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700224 * @param subjectClassKey subjectKey class key
225 * @param subjectKey subjectKey key
226 * @param request network configuration JSON rooted at the top node
Jian Licc730a62016-05-10 16:36:16 -0700227 * @return 200 OK
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700228 * @throws IOException if unable to parse the request
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700229 */
230 @POST
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700231 @Path("{subjectClassKey}/{subjectKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700232 @Consumes(MediaType.APPLICATION_JSON)
233 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700234 public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
235 @PathParam("subjectKey") String subjectKey,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700236 InputStream request) throws IOException {
237 NetworkConfigService service = get(NetworkConfigService.class);
238 ObjectNode root = (ObjectNode) mapper().readTree(request);
239 consumeSubjectJson(service, root,
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700240 service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
241 subjectClassKey);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700242 return Response.ok().build();
243 }
244
245 /**
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700246 * Upload specific network configuration for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700247 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700248 * @param subjectClassKey subjectKey class key
249 * @param subjectKey subjectKey key
250 * @param configKey configuration class key
251 * @param request network configuration JSON rooted at the top node
Jian Licc730a62016-05-10 16:36:16 -0700252 * @return 200 OK
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700253 * @throws IOException if unable to parse the request
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700254 */
255 @POST
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700256 @Path("{subjectClassKey}/{subjectKey}/{configKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700257 @Consumes(MediaType.APPLICATION_JSON)
258 @SuppressWarnings("unchecked")
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700259 public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
260 @PathParam("subjectKey") String subjectKey,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700261 @PathParam("configKey") String configKey,
262 InputStream request) throws IOException {
263 NetworkConfigService service = get(NetworkConfigService.class);
Jonathan Hartb11c4d02016-03-23 09:05:44 -0700264 JsonNode root = mapper().readTree(request);
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800265 service.applyConfig(subjectClassKey,
266 service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
267 configKey, root);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700268 return Response.ok().build();
269 }
270
271 private void consumeJson(NetworkConfigService service, ObjectNode classNode,
272 SubjectFactory subjectFactory) {
273 classNode.fieldNames().forEachRemaining(s ->
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700274 consumeSubjectJson(service, (ObjectNode) classNode.path(s),
275 subjectFactory.createSubject(s),
276 subjectFactory.subjectClassKey()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700277 }
278
279 private void consumeSubjectJson(NetworkConfigService service,
Jonathan Hart111b42b2015-07-14 13:28:05 -0700280 ObjectNode subjectNode, Object subject,
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800281 String subjectClassKey) {
282 subjectNode.fieldNames().forEachRemaining(configKey ->
283 service.applyConfig(subjectClassKey, subject, configKey, subjectNode.path(configKey)));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700284 }
285
286
Thomas Vachuska6f350ed2016-01-08 09:53:03 -0800287 // FIXME: Refactor to allow queued configs to be removed
288
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700289 /**
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700290 * Clear entire network configuration base.
291 *
Jian Lic2a542b2016-05-10 11:48:19 -0700292 * @return 204 NO CONTENT
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700293 */
294 @DELETE
295 @SuppressWarnings("unchecked")
296 public Response delete() {
297 NetworkConfigService service = get(NetworkConfigService.class);
298 service.getSubjectClasses()
299 .forEach(subjectClass -> service.getSubjects(subjectClass)
300 .forEach(subject -> service.getConfigs(subject)
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700301 .forEach(config -> service.removeConfig(subject, config.getClass()))));
Jian Lic2a542b2016-05-10 11:48:19 -0700302 return Response.noContent().build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700303 }
304
305 /**
306 * Clear all network configurations for a subject class.
307 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700308 * @param subjectClassKey subject class key
Jian Lic2a542b2016-05-10 11:48:19 -0700309 * @return 204 NO CONTENT
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700310 */
311 @DELETE
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700312 @Path("{subjectClassKey}")
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700313 @SuppressWarnings("unchecked")
Jian Lic2a542b2016-05-10 11:48:19 -0700314 public Response delete(@PathParam("subjectClassKey") String subjectClassKey) {
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700315 NetworkConfigService service = get(NetworkConfigService.class);
Ray Milkeyb9fe25d2015-11-18 15:54:44 -0800316 service.getSubjects(service.getSubjectFactory(subjectClassKey).subjectClass())
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700317 .forEach(subject -> service.getConfigs(subject)
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700318 .forEach(config -> service.removeConfig(subject, config.getClass())));
Jian Lic2a542b2016-05-10 11:48:19 -0700319 return Response.noContent().build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700320 }
321
322 /**
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700323 * Clear all network configurations for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700324 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700325 * @param subjectClassKey subjectKey class key
326 * @param subjectKey subjectKey key
Jian Lic2a542b2016-05-10 11:48:19 -0700327 * @return 204 NO CONTENT
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700328 */
329 @DELETE
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700330 @Path("{subjectClassKey}/{subjectKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700331 @SuppressWarnings("unchecked")
Jian Lic2a542b2016-05-10 11:48:19 -0700332 public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700333 @PathParam("subjectKey") String subjectKey) {
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700334 NetworkConfigService service = get(NetworkConfigService.class);
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700335 Object s = service.getSubjectFactory(subjectClassKey).createSubject(subjectKey);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700336 service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
Jian Lic2a542b2016-05-10 11:48:19 -0700337 return Response.noContent().build();
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700338 }
339
340 /**
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700341 * Clear specific network configuration for a subjectKey.
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700342 *
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700343 * @param subjectClassKey subjectKey class key
344 * @param subjectKey subjectKey key
345 * @param configKey configuration class key
Jian Lic2a542b2016-05-10 11:48:19 -0700346 * @return 204 NO CONTENT
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700347 */
348 @DELETE
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700349 @Path("{subjectClassKey}/{subjectKey}/{configKey}")
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700350 @SuppressWarnings("unchecked")
Jian Lic2a542b2016-05-10 11:48:19 -0700351 public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700352 @PathParam("subjectKey") String subjectKey,
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700353 @PathParam("configKey") String configKey) {
354 NetworkConfigService service = get(NetworkConfigService.class);
Thomas Vachuskaea5adc62015-10-07 11:52:30 -0700355 service.removeConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
356 service.getConfigClass(subjectClassKey, configKey));
Jian Lic2a542b2016-05-10 11:48:19 -0700357 return Response.noContent().build();
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700358 }
359
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700360}