blob: 2c2fd774defe2d402a0684593a2655b95e464c38 [file] [log] [blame]
Carmelo Casconeb5324e72018-11-25 02:26:32 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
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 */
16
17package org.onosproject.pipelines.fabric.pipeliner;
18
19import com.google.common.collect.Lists;
Carmelo Casconed06a8512018-12-02 16:34:20 -080020import org.onlab.packet.VlanId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080021import org.onosproject.net.DeviceId;
22import org.onosproject.net.PortNumber;
23import org.onosproject.net.flow.DefaultTrafficSelector;
24import org.onosproject.net.flow.DefaultTrafficTreatment;
25import org.onosproject.net.flow.TrafficSelector;
26import org.onosproject.net.flow.TrafficTreatment;
27import org.onosproject.net.flow.criteria.Criterion;
28import org.onosproject.net.flow.criteria.PiCriterion;
29import org.onosproject.net.flow.criteria.VlanIdCriterion;
30import org.onosproject.net.flow.instructions.Instruction;
Carmelo Casconed06a8512018-12-02 16:34:20 -080031import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080032import org.onosproject.net.flowobjective.DefaultNextTreatment;
33import org.onosproject.net.flowobjective.NextObjective;
34import org.onosproject.net.flowobjective.NextTreatment;
35import org.onosproject.net.flowobjective.Objective;
36import org.onosproject.net.flowobjective.ObjectiveError;
37import org.onosproject.net.group.DefaultGroupBucket;
38import org.onosproject.net.group.DefaultGroupDescription;
39import org.onosproject.net.group.DefaultGroupKey;
40import org.onosproject.net.group.GroupBucket;
41import org.onosproject.net.group.GroupBuckets;
42import org.onosproject.net.group.GroupDescription;
43import org.onosproject.net.group.GroupKey;
44import org.onosproject.net.pi.model.PiTableId;
45import org.onosproject.net.pi.runtime.PiAction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080046import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070047import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080048import org.onosproject.net.pi.runtime.PiGroupKey;
49import org.onosproject.pipelines.fabric.FabricCapabilities;
50import org.onosproject.pipelines.fabric.FabricConstants;
51import org.onosproject.pipelines.fabric.FabricUtils;
52
53import java.util.Collection;
54import java.util.List;
55import java.util.Objects;
56import java.util.Set;
57import java.util.stream.Collectors;
58
59import static java.lang.String.format;
Carmelo Casconed06a8512018-12-02 16:34:20 -080060import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080061import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
62import static org.onosproject.pipelines.fabric.FabricUtils.criterion;
63import static org.onosproject.pipelines.fabric.FabricUtils.l2Instruction;
64import static org.onosproject.pipelines.fabric.FabricUtils.outputPort;
65
66/**
67 * ObjectiveTranslator implementation for NextObjective.
68 */
69class NextObjectiveTranslator
70 extends AbstractObjectiveTranslator<NextObjective> {
71
Carmelo Cascone45cc0862018-11-26 11:50:41 -080072 private static final String XCONNECT = "xconnect";
73
Carmelo Casconeb5324e72018-11-25 02:26:32 -080074 NextObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
75 super(deviceId, capabilities);
76 }
77
78 @Override
79 public ObjectiveTranslation doTranslate(NextObjective obj)
80 throws FabricPipelinerException {
81
82 final ObjectiveTranslation.Builder resultBuilder =
83 ObjectiveTranslation.builder();
84
85 switch (obj.type()) {
86 case SIMPLE:
87 simpleNext(obj, resultBuilder, false);
88 break;
89 case HASHED:
90 hashedNext(obj, resultBuilder);
91 break;
92 case BROADCAST:
Carmelo Cascone45cc0862018-11-26 11:50:41 -080093 if (isXconnect(obj)) {
94 xconnectNext(obj, resultBuilder);
95 } else {
96 multicastNext(obj, resultBuilder);
97 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -080098 break;
99 default:
100 log.warn("Unsupported NextObjective type '{}'", obj);
101 return ObjectiveTranslation.ofError(ObjectiveError.UNSUPPORTED);
102 }
103
104 if (!isGroupModifyOp(obj)) {
105 // Generate next VLAN rules.
106 nextVlan(obj, resultBuilder);
107 }
108
109 return resultBuilder.build();
110 }
111
112 private void nextVlan(NextObjective obj,
113 ObjectiveTranslation.Builder resultBuilder)
114 throws FabricPipelinerException {
Carmelo Casconed06a8512018-12-02 16:34:20 -0800115
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700116 // Set the egress VLAN for this next ID. Expect a VLAN_ID instruction
117 // in the treatment, or use what's in meta.
118 final List<ModVlanIdInstruction> vlanInstructions = defaultNextTreatments(
119 obj.nextTreatments(), false).stream()
120 .map(t -> (ModVlanIdInstruction) l2Instruction(t.treatment(), VLAN_ID))
121 .filter(Objects::nonNull)
122 .collect(Collectors.toList());
Carmelo Casconed06a8512018-12-02 16:34:20 -0800123 final VlanIdCriterion vlanIdCriterion = obj.meta() == null ? null
124 : (VlanIdCriterion) criterion(obj.meta().criteria(), Criterion.Type.VLAN_VID);
Carmelo Casconed06a8512018-12-02 16:34:20 -0800125
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700126 VlanId vlanId;
127 if (vlanInstructions.isEmpty() && vlanIdCriterion == null) {
128 // No VLAN_ID to apply.
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800129 return;
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700130 } else if (!vlanInstructions.isEmpty()) {
131 // Give priority to what found in the instructions.
132 // Expect the same VLAN ID for all instructions.
133 final Set<VlanId> vlanIds = vlanInstructions.stream()
134 .map(ModVlanIdInstruction::vlanId)
135 .collect(Collectors.toSet());
136 if (obj.nextTreatments().size() != vlanInstructions.size() ||
137 vlanIds.size() != 1) {
Carmelo Casconed06a8512018-12-02 16:34:20 -0800138 throw new FabricPipelinerException(
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700139 "Inconsistent VLAN_ID instructions, cannot process " +
140 "next_vlan rule. It is required that all " +
141 "treatments have the same VLAN_ID instruction.");
Carmelo Casconed06a8512018-12-02 16:34:20 -0800142 }
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700143 vlanId = vlanIds.iterator().next();
144 } else {
145 // Use the value in meta.
146 // FIXME: there should be no need to generate a next_vlan rule for
147 // the value found in meta. Meta describes the fields that were
148 // expected to be matched in previous pipeline stages, i.e.
149 // existing packet fields. But, for some reason, if we remove this
150 // rule, traffic is not forwarded at spines. We might need to look
151 // at the way default VLANs are handled in fabric.p4.
152 vlanId = vlanIdCriterion.vlanId();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800153 }
154
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800155 final TrafficSelector selector = nextIdSelector(obj.id());
156 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Carmelo Casconed06a8512018-12-02 16:34:20 -0800157 .setVlanId(vlanId)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800158 .build();
159
160 resultBuilder.addFlowRule(flowRule(
161 obj, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
162 selector, treatment));
163 }
164
165 private void simpleNext(NextObjective obj,
166 ObjectiveTranslation.Builder resultBuilder,
167 boolean forceSimple)
168 throws FabricPipelinerException {
169
170 if (capabilities.hasHashedTable()) {
171 // Use hashed table when possible.
172 hashedNext(obj, resultBuilder);
173 return;
174 }
175
176 if (obj.nextTreatments().isEmpty()) {
177 // Do nothing.
178 return;
179 } else if (!forceSimple && obj.nextTreatments().size() != 1) {
180 throw new FabricPipelinerException(format(
181 "SIMPLE NextObjective should contain only 1 treatment, found %d",
182 obj.nextTreatments().size()), ObjectiveError.BADPARAMS);
183 }
184
185 final TrafficSelector selector = nextIdSelector(obj.id());
186
Carmelo Casconed06a8512018-12-02 16:34:20 -0800187 final List<DefaultNextTreatment> treatments = defaultNextTreatments(
188 obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800189
190 if (forceSimple && treatments.size() > 1) {
191 log.warn("Forcing SIMPLE behavior for NextObjective with {} treatments []",
192 treatments.size(), obj);
193 }
194
195 // If not forcing, we are essentially extracting the only available treatment.
Carmelo Casconed06a8512018-12-02 16:34:20 -0800196 final TrafficTreatment treatment = defaultNextTreatments(
197 obj.nextTreatments(), true).get(0).treatment();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800198
199 resultBuilder.addFlowRule(flowRule(
200 obj, FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE,
201 selector, treatment));
202
203 handleEgress(obj, treatment, resultBuilder, false);
204 }
205
206 private void hashedNext(NextObjective obj,
207 ObjectiveTranslation.Builder resultBuilder)
208 throws FabricPipelinerException {
209
210 if (!capabilities.hasHashedTable()) {
211 simpleNext(obj, resultBuilder, true);
212 return;
213 }
214
215 // Updated result builder with hashed group.
216 final int groupId = selectGroup(obj, resultBuilder);
217
218 if (isGroupModifyOp(obj)) {
219 // No changes to flow rules.
220 return;
221 }
222
223 final TrafficSelector selector = nextIdSelector(obj.id());
224 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700225 .piTableAction(PiActionProfileGroupId.of(groupId))
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800226 .build();
227
228 resultBuilder.addFlowRule(flowRule(
229 obj, FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
230 selector, treatment));
231 }
232
233 private void handleEgress(NextObjective obj, TrafficTreatment treatment,
234 ObjectiveTranslation.Builder resultBuilder,
235 boolean strict)
236 throws FabricPipelinerException {
237 final PortNumber outPort = outputPort(treatment);
238 final Instruction popVlanInst = l2Instruction(treatment, VLAN_POP);
239 if (popVlanInst != null && outPort != null) {
240 if (strict && treatment.allInstructions().size() > 2) {
241 throw new FabricPipelinerException(
242 "Treatment contains instructions other " +
243 "than OUTPUT and VLAN_POP, cannot generate " +
244 "egress rules");
245 }
246 egressVlanPop(outPort, obj, resultBuilder);
247 }
248 }
249
250 private void egressVlanPop(PortNumber outPort, NextObjective obj,
251 ObjectiveTranslation.Builder resultBuilder)
252 throws FabricPipelinerException {
253
254 if (obj.meta() == null) {
255 throw new FabricPipelinerException(
256 "Cannot process egress pop VLAN rule, NextObjective has null meta",
257 ObjectiveError.BADPARAMS);
258 }
259
260 final VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion(
261 obj.meta(), Criterion.Type.VLAN_VID);
262 if (vlanIdCriterion == null) {
263 throw new FabricPipelinerException(
264 "Cannot process egress pop VLAN rule, missing VLAN_VID criterion " +
265 "in NextObjective meta",
266 ObjectiveError.BADPARAMS);
267 }
268
269 final PiCriterion egressVlanTableMatch = PiCriterion.builder()
270 .matchExact(FabricConstants.HDR_EG_PORT, outPort.toLong())
271 .build();
272 final TrafficSelector selector = DefaultTrafficSelector.builder()
273 .matchPi(egressVlanTableMatch)
274 .matchVlanId(vlanIdCriterion.vlanId())
275 .build();
276 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
277 .popVlan()
278 .build();
279
280 resultBuilder.addFlowRule(flowRule(
281 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
282 selector, treatment));
283 }
284
285 private TrafficSelector nextIdSelector(int nextId) {
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800286 return nextIdSelectorBuilder(nextId).build();
287 }
288
289 private TrafficSelector.Builder nextIdSelectorBuilder(int nextId) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800290 final PiCriterion nextIdCriterion = PiCriterion.builder()
291 .matchExact(FabricConstants.HDR_NEXT_ID, nextId)
292 .build();
293 return DefaultTrafficSelector.builder()
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800294 .matchPi(nextIdCriterion);
295 }
296
297 private void xconnectNext(NextObjective obj, ObjectiveTranslation.Builder resultBuilder)
298 throws FabricPipelinerException {
299
300 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800301 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800302
303 final List<PortNumber> outPorts = defaultNextTreatments.stream()
304 .map(DefaultNextTreatment::treatment)
305 .map(FabricUtils::outputPort)
306 .filter(Objects::nonNull)
307 .collect(Collectors.toList());
308
309 if (outPorts.size() != 2) {
310 throw new FabricPipelinerException(format(
311 "Handling XCONNECT with %d treatments (ports), but expected is 2",
312 defaultNextTreatments.size()), ObjectiveError.UNSUPPORTED);
313 }
314
315 final PortNumber port1 = outPorts.get(0);
316 final PortNumber port2 = outPorts.get(1);
317 final TrafficSelector selector1 = nextIdSelectorBuilder(obj.id())
318 .matchInPort(port1)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800319 .build();
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800320 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
321 .setOutput(port2)
322 .build();
323 final TrafficSelector selector2 = nextIdSelectorBuilder(obj.id())
324 .matchInPort(port2)
325 .build();
326 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
327 .setOutput(port1)
328 .build();
329
330 resultBuilder.addFlowRule(flowRule(
331 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
332 selector1, treatment1));
333 resultBuilder.addFlowRule(flowRule(
334 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
335 selector2, treatment2));
336
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800337 }
338
339 private void multicastNext(NextObjective obj,
340 ObjectiveTranslation.Builder resultBuilder)
341 throws FabricPipelinerException {
342
343 // Create ALL group that will be translated to a PRE multicast entry.
344 final int groupId = allGroup(obj, resultBuilder);
345
346 if (isGroupModifyOp(obj)) {
347 // No changes to flow rules.
348 return;
349 }
350
351 final TrafficSelector selector = nextIdSelector(obj.id());
352 final PiActionParam groupIdParam = new PiActionParam(
353 FabricConstants.GROUP_ID, groupId);
354 final PiAction setMcGroupAction = PiAction.builder()
355 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID)
356 .withParameter(groupIdParam)
357 .build();
358 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
359 .piTableAction(setMcGroupAction)
360 .build();
361
362 resultBuilder.addFlowRule(flowRule(
363 obj, FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST,
364 selector, treatment));
365 }
366
367 private int selectGroup(NextObjective obj,
368 ObjectiveTranslation.Builder resultBuilder)
369 throws FabricPipelinerException {
370
371 final PiTableId hashedTableId = FabricConstants.FABRIC_INGRESS_NEXT_HASHED;
372 final List<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800373 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800374 final List<TrafficTreatment> piTreatments = Lists.newArrayList();
375
376 for (DefaultNextTreatment t : defaultNextTreatments) {
377 // Map treatment to PI...
378 piTreatments.add(mapTreatmentToPiIfNeeded(t.treatment(), hashedTableId));
379 // ...and handle egress if necessary.
380 handleEgress(obj, t.treatment(), resultBuilder, false);
381 }
382
383 final List<GroupBucket> bucketList = piTreatments.stream()
384 .map(DefaultGroupBucket::createSelectGroupBucket)
385 .collect(Collectors.toList());
386
387 final int groupId = obj.id();
388 final PiGroupKey groupKey = new PiGroupKey(
389 hashedTableId,
390 FabricConstants.FABRIC_INGRESS_NEXT_HASHED_SELECTOR,
391 groupId);
392
393 resultBuilder.addGroup(new DefaultGroupDescription(
394 deviceId,
395 GroupDescription.Type.SELECT,
396 new GroupBuckets(bucketList),
397 groupKey,
398 groupId,
399 obj.appId()));
400
401 return groupId;
402 }
403
404 private int allGroup(NextObjective obj,
405 ObjectiveTranslation.Builder resultBuilder)
406 throws FabricPipelinerException {
407
408 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800409 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800410 // No need to map treatments to PI as translation of ALL groups to PRE
411 // multicast entries is based solely on the output port.
412 for (DefaultNextTreatment t : defaultNextTreatments) {
413 handleEgress(obj, t.treatment(), resultBuilder, true);
414 }
415
416 // FIXME: this implementation supports only the case in which each
417 // switch interface is associated with only one VLAN, otherwise we would
418 // need to support replicating multiple times the same packet for the
419 // same port while setting different VLAN IDs. Hence, collect in a set.
420 final Set<PortNumber> outPorts = defaultNextTreatments.stream()
421 .map(DefaultNextTreatment::treatment)
422 .map(FabricUtils::outputPort)
423 .filter(Objects::nonNull)
424 .collect(Collectors.toSet());
425
426 if (outPorts.size() != defaultNextTreatments.size()) {
427 throw new FabricPipelinerException(format(
428 "Found BROADCAST NextObjective with %d treatments but " +
429 "found only %d distinct OUTPUT port numbers, cannot " +
430 "translate to ALL groups",
431 defaultNextTreatments.size(), outPorts.size()),
432 ObjectiveError.UNSUPPORTED);
433 }
434
435 final List<GroupBucket> bucketList = outPorts.stream()
436 .map(p -> DefaultTrafficTreatment.builder().setOutput(p).build())
437 .map(DefaultGroupBucket::createAllGroupBucket)
438 .collect(Collectors.toList());
439 // FIXME: remove once support for clone sessions is available
440 // Right now we add a CPU port to all multicast groups. The egress
441 // pipeline is expected to drop replicated packets to the CPU if a clone
442 // was not requested in the ingress pipeline.
443 bucketList.add(
444 DefaultGroupBucket.createAllGroupBucket(
445 DefaultTrafficTreatment.builder()
446 .setOutput(PortNumber.CONTROLLER)
447 .build()));
448
449 final int groupId = obj.id();
450 // Use DefaultGroupKey instead of PiGroupKey as we don't have any
451 // action profile to apply to the groups of ALL type.
452 final GroupKey groupKey = new DefaultGroupKey(
453 FabricPipeliner.KRYO.serialize(groupId));
454
455 resultBuilder.addGroup(
456 new DefaultGroupDescription(
457 deviceId,
458 GroupDescription.Type.ALL,
459 new GroupBuckets(bucketList),
460 groupKey,
461 groupId,
462 obj.appId()));
463
464 return groupId;
465 }
466
Carmelo Casconed06a8512018-12-02 16:34:20 -0800467 private List<DefaultNextTreatment> defaultNextTreatments(
468 Collection<NextTreatment> nextTreatments, boolean strict)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800469 throws FabricPipelinerException {
470 final List<DefaultNextTreatment> defaultNextTreatments = Lists.newArrayList();
471 final List<NextTreatment> unsupportedNextTreatments = Lists.newArrayList();
472 for (NextTreatment n : nextTreatments) {
473 if (n.type() == NextTreatment.Type.TREATMENT) {
474 defaultNextTreatments.add((DefaultNextTreatment) n);
475 } else {
476 unsupportedNextTreatments.add(n);
477 }
478 }
Carmelo Casconed06a8512018-12-02 16:34:20 -0800479 if (strict && !unsupportedNextTreatments.isEmpty()) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800480 throw new FabricPipelinerException(format(
481 "Unsupported NextTreatments: %s",
482 unsupportedNextTreatments));
483 }
484 return defaultNextTreatments;
485 }
486
Carmelo Casconed06a8512018-12-02 16:34:20 -0800487 private TrafficTreatment getFirstDefaultNextTreatmentIfAny(
488 Collection<NextTreatment> nextTreatments)
489 throws FabricPipelinerException {
490 final Collection<DefaultNextTreatment> nexts = defaultNextTreatments(nextTreatments, false);
491 return nexts.isEmpty() ? null : nexts.iterator().next().treatment();
492 }
493
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800494 private boolean isGroupModifyOp(NextObjective obj) {
495 // If operation is ADD_TO_EXIST or REMOVE_FROM_EXIST, it means we modify
496 // group buckets only, no changes for flow rules.
497 return obj.op() == Objective.Operation.ADD_TO_EXISTING ||
498 obj.op() == Objective.Operation.REMOVE_FROM_EXISTING;
499 }
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800500
501 private boolean isXconnect(NextObjective obj) {
502 return obj.appId().name().contains(XCONNECT);
503 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800504}