diff options
Diffstat (limited to 'srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy')
4 files changed, 513 insertions, 0 deletions
diff --git a/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/PolicyCustomizer.java b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/PolicyCustomizer.java new file mode 100644 index 000000000..d8e09e84d --- /dev/null +++ b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/PolicyCustomizer.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018 Bell Canada, Pantheon Technologies and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.srv6.write.policy; + +import com.google.common.base.Preconditions; +import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer; +import io.fd.hc2vpp.fib.management.FibManagementIIds; +import io.fd.hc2vpp.srv6.Srv6PolicyIIds; +import io.fd.hc2vpp.srv6.util.CandidatePathContextManager; +import io.fd.hc2vpp.srv6.util.PolicyContextManager; +import io.fd.hc2vpp.srv6.util.Srv6Util; +import io.fd.hc2vpp.srv6.write.policy.request.PolicyDeleteRequest; +import io.fd.hc2vpp.srv6.write.policy.request.PolicyWriteRequest; +import io.fd.hc2vpp.srv6.write.policy.request.dto.SidList; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.BindingSidAllocMode; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.DataplaneType; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.ProvisioningMethodConfig; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.candidate.paths.candidate.paths.CandidatePath; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.named.segment.lists.named.segment.lists.NamedSegmentList; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.named.segment.lists.named.segment.lists.NamedSegmentListKey; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.path.segment.list.properties.segment.lists.SegmentList; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.policies.policies.Policy; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.policies.policies.PolicyKey; +import org.opendaylight.yang.gen.v1.urn.hc2vpp.params.xml.ns.yang.vpp.oc.srte.policy.rev180514.VppSrPolicyAugmentation; +import org.opendaylight.yang.gen.v1.urn.hc2vpp.params.xml.ns.yang.vpp.oc.srte.policy.rev180514.sr.policy.Config; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.fib.table.management.rev180521.VniReference; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.fib.table.management.rev180521.vpp.fib.table.management.fib.tables.Table; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.fib.table.management.rev180521.vpp.fib.table.management.fib.tables.TableKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; + +public class PolicyCustomizer extends FutureJVppCustomizer + implements ListWriterCustomizer<Policy, PolicyKey> { + + private final PolicyContextManager policyContext; + private final CandidatePathContextManager candidateContext; + + public PolicyCustomizer(@Nonnull final FutureJVppCore futureJVppCore, + @Nonnull final PolicyContextManager policyContext, + @Nonnull final CandidatePathContextManager candidateContext) { + super(futureJVppCore); + this.policyContext = policyContext; + this.candidateContext = candidateContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Policy> instanceIdentifier, + @Nonnull final Policy policy, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + try { + // Fib table must be created beforehand. First we check if all data is present, then we verify the existence + // of FIB table in current configuration + VppSrPolicyAugmentation policyAugmentation = policy.getAugmentation(VppSrPolicyAugmentation.class); + + if (policyAugmentation != null && policyAugmentation.getVppSrPolicy() != null && + policyAugmentation.getVppSrPolicy().getConfig() != null) { + Config config = policyAugmentation.getVppSrPolicy().getConfig(); + TableKey tableKey = new TableKey(config.getAddressFamily(), new VniReference(config.getTableId())); + KeyedInstanceIdentifier<Table, TableKey> vrfIid = + FibManagementIIds.FM_FIB_TABLES.child(Table.class, tableKey); + if (!writeContext.readAfter(vrfIid).isPresent()) { + throw new IllegalArgumentException( + String.format("VRF table: %s not found. Create table before writing policy.", tableKey)); + } + if (policy.getCandidatePaths() != null && !policy.getCandidatePaths().getCandidatePath().isEmpty()) { + bindWriteRequest(config, policy.getCandidatePaths().getCandidatePath(), writeContext) + .write(instanceIdentifier); + Ipv6Address bsid = Srv6Util.extractBsid(instanceIdentifier, writeContext, true); + policyContext.addPolicy(policy.getName(), policy.getColor(), policy.getEndpoint().getIpv6Address(), + bsid, writeContext.getMappingContext()); + } + } else { + throw new ReadFailedException(instanceIdentifier, + new Throwable("VppSrPolicyAugmentation and/or VppSrPolicy missing.")); + } + } catch (ReadFailedException e) { + throw new WriteFailedException.CreateFailedException(instanceIdentifier, policy, e); + } + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Policy> instanceIdentifier, + @Nonnull final Policy policy, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + if (policy.getCandidatePaths() != null && !policy.getCandidatePaths().getCandidatePath().isEmpty()) { + bindDeleteRequest(policy.getCandidatePaths().getCandidatePath(), writeContext).delete(instanceIdentifier); + Ipv6Address bsid = Srv6Util.extractBsid(instanceIdentifier, writeContext, false); + Preconditions.checkNotNull(bsid, "BSID must not be null"); + policyContext.removePolicy(bsid, writeContext.getMappingContext()); + } + } + + private PolicyDeleteRequest bindDeleteRequest(final @Nonnull List<CandidatePath> candidatePaths, + final @Nonnull WriteContext writeContext) { + final PolicyDeleteRequest request = new PolicyDeleteRequest(getFutureJVpp()); + + Optional<CandidatePath> candidatePathOptional = parseBestCandidate(candidatePaths); + Preconditions.checkArgument(candidatePathOptional.isPresent(), + "Could not parse best Candidate path from list: {}", candidatePaths); + + CandidatePath selectedPath = candidatePathOptional.get(); + if (selectedPath.getBindingSid() != null && selectedPath.getBindingSid().getConfig() != null) { + org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.binding.sid.properties.binding.sid.Config + sidConfig = selectedPath.getBindingSid().getConfig(); + + if (sidConfig.getType() == DataplaneType.Srv6 && + sidConfig.getAllocMode() == BindingSidAllocMode.Explicit && sidConfig.getValue() != null && + sidConfig.getValue().getIpAddress() != null && + sidConfig.getValue().getIpAddress().getIpv6Address() != null) { + Ipv6Address bsid = selectedPath.getBindingSid().getConfig().getValue().getIpAddress().getIpv6Address(); + request.setBindingSidAddress(bsid); + candidateContext.removeCandidatePath(bsid, writeContext.getMappingContext()); + } + } + return request; + } + + private PolicyWriteRequest bindWriteRequest(@Nonnull final Config config, + final List<CandidatePath> candidatePaths, + final WriteContext writeContext) { + final PolicyWriteRequest request = new PolicyWriteRequest(getFutureJVpp()); + request.setFibTableIndex(config.getTableId().getValue().intValue()); + request.setPolicyBehavior(config.getPolicyBehavior()); + request.setPolicyType(config.getPolicyType()); + + Optional<CandidatePath> candidatePathOptional = parseBestCandidate(candidatePaths); + Preconditions.checkArgument(candidatePathOptional.isPresent(), + "Could not parse best Candidate path from list: {}", candidatePaths); + + CandidatePath selectedPath = candidatePathOptional.get(); + if (selectedPath.getBindingSid() != null && selectedPath.getBindingSid().getConfig() != null) { + org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.binding.sid.properties.binding.sid.Config + sidConfig = selectedPath.getBindingSid().getConfig(); + + if (sidConfig.getType() == DataplaneType.Srv6 && + sidConfig.getAllocMode() == BindingSidAllocMode.Explicit && sidConfig.getValue() != null && + sidConfig.getValue().getIpAddress() != null && + sidConfig.getValue().getIpAddress().getIpv6Address() != null) { + Ipv6Address bsid = selectedPath.getBindingSid().getConfig().getValue().getIpAddress().getIpv6Address(); + request.setBindingSidAddress(bsid); + candidateContext.addCandidatePath(bsid, selectedPath.getName(), selectedPath.getProvisioningMethod(), + selectedPath.getPreference(), selectedPath.getDistinguisher(), + writeContext.getMappingContext()); + } + } + if (selectedPath.getSegmentLists() != null && selectedPath.getSegmentLists().getSegmentList() != null) { + request.setSegments(readSegmentLists(selectedPath.getSegmentLists().getSegmentList(), writeContext)); + } + + return request; + } + + private List<SidList> readSegmentLists(final List<SegmentList> segmentLists, final WriteContext writeContext) { + List<SidList> sidLists = new ArrayList<>(); + + segmentLists.forEach(segmentList -> { + com.google.common.base.Optional<NamedSegmentList> namedSegmentListOptional = writeContext.readAfter( + Srv6PolicyIIds.SR_TE_NSLS.child(NamedSegmentList.class, + new NamedSegmentListKey(segmentList.getName()))); + + if (namedSegmentListOptional.isPresent()) { + sidLists.add(SidList.builder() + .setNamedSegmentList(namedSegmentListOptional.get()) + .setWeight(segmentList.getConfig().getWeight()) + .build()); + } + }); + return sidLists; + } + + /** + * Selects best Candidate based on Preference value (the higher preference the better), + * only static configuration is supported for now (provisioning-method must be equal to provisioning-method-config). + * + * Based on Segment Routing Policy for Traffic Engineering + * https://tools.ietf.org/html/draft-filsfils-spring-segment-routing-policy-00 + * + * @param candidatePaths List of available CandidatePaths + * @return Optional of CandidatePath + */ + private Optional<CandidatePath> parseBestCandidate(final List<CandidatePath> candidatePaths) { + return candidatePaths.stream() + .filter(candidatePath -> candidatePath.getProvisioningMethod().equals(ProvisioningMethodConfig.class)) + .max(Comparator.comparingLong(CandidatePath::getPreference)); + } +} diff --git a/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyDeleteRequest.java b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyDeleteRequest.java new file mode 100644 index 000000000..71b9d72e8 --- /dev/null +++ b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyDeleteRequest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Bell Canada, Pantheon Technologies and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.srv6.write.policy.request; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.srv6.util.JVppRequest; +import io.fd.hc2vpp.srv6.write.DeleteRequest; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SrPolicyDel; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import io.fd.vpp.jvpp.core.types.Srv6Sid; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class PolicyDeleteRequest extends JVppRequest implements DeleteRequest { + + /** + * Binding SID of the policy + */ + private Ipv6Address bindingSidAddress; + + public PolicyDeleteRequest(final FutureJVppCore api) { + super(api); + } + + @Override + public void checkValid() { + checkNotNull(bindingSidAddress, "Binding sid address not set"); + } + + @Override + public void delete(final InstanceIdentifier<?> identifier) throws WriteFailedException { + checkValid(); + final SrPolicyDel request = new SrPolicyDel(); + Srv6Sid bsid = new Srv6Sid(); + bsid.addr = ipv6AddressNoZoneToArray(bindingSidAddress); + request.bsidAddr = bsid; + getReplyForDelete(getApi().srPolicyDel(request).toCompletableFuture(), identifier); + } + + public void setBindingSidAddress( + final Ipv6Address bindingSidAddress) { + this.bindingSidAddress = bindingSidAddress; + } +} diff --git a/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyWriteRequest.java b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyWriteRequest.java new file mode 100644 index 000000000..6dc3e867d --- /dev/null +++ b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/PolicyWriteRequest.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 Bell Canada, Pantheon Technologies and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.srv6.write.policy.request; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.srv6.util.JVppRequest; +import io.fd.hc2vpp.srv6.write.WriteRequest; +import io.fd.hc2vpp.srv6.write.policy.request.dto.SidList; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.SrPolicyAdd; +import io.fd.vpp.jvpp.core.dto.SrPolicyMod; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import io.fd.vpp.jvpp.core.types.Srv6Sid; +import io.fd.vpp.jvpp.core.types.Srv6SidList; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.named.segment.lists.named.segment.lists.named.segment.list.Segments; +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.named.segment.lists.named.segment.lists.named.segment.list.segments.Segment; +import org.opendaylight.yang.gen.v1.urn.hc2vpp.params.xml.ns.yang.vpp.oc.srte.policy.rev180514.SegmentRoutingPolicyBehavior; +import org.opendaylight.yang.gen.v1.urn.hc2vpp.params.xml.ns.yang.vpp.oc.srte.policy.rev180514.SegmentRoutingPolicyType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PolicyWriteRequest extends JVppRequest implements WriteRequest { + + /** + * Types of modification operations + */ + private static final int ADD_NEW = 1; + private static final int DELETE_EXISTING = 2; + private static final int MODIFY_WEIGHT = 3; + + private static final Logger LOG = LoggerFactory.getLogger(PolicyWriteRequest.class); + + /** + * Binding SID of the policy + */ + private Ipv6Address bindingSidAddress; + + /** + * Behavior used by this policy. Either inserts to existing header or encapsulate to new one + */ + private SegmentRoutingPolicyBehavior policyBehavior; + + /** + * Revelant if multiple segment lists are used. Default is load-balancing, spray will send traffic to every segment + */ + private SegmentRoutingPolicyType policyType; + + /** + * FIB table where entry should be installed + */ + private int fibTableIndex; + + /** + * List of SidList + */ + private List<SidList> segments; + + public PolicyWriteRequest(final FutureJVppCore api) { + super(api); + } + + private static Srv6Sid[] convertSegmentsToByteArray(final Segments segments) { + List<Srv6Sid> sidList = new ArrayList<>(); + segments.getSegment().stream().sorted(Comparator.comparingLong(Segment::getIndex)) + .map(segment -> segment.getConfig().getSidValue().getIpAddress().getIpv6Address()) + .forEach(ipv6Address -> { + Srv6Sid sid = new Srv6Sid(); + sid.addr = AddressTranslator.INSTANCE.ipv6AddressNoZoneToArray(ipv6Address); + sidList.add(sid); + }); + return sidList.toArray(new Srv6Sid[0]); + } + + @Override + public void write(final InstanceIdentifier<?> identifier) throws WriteFailedException { + checkValid(); + + final SrPolicyAdd createRequest = new SrPolicyAdd(); + createRequest.bsidAddr = ipv6AddressNoZoneToArray(bindingSidAddress); + createRequest.isEncap = (byte) policyBehavior.getIntValue(); + createRequest.type = (byte) policyType.getIntValue(); + createRequest.fibTable = fibTableIndex; + + SidList firstSidList = segments.get(0); + + createRequest.sids = new Srv6SidList(); + createRequest.sids.numSids = (byte) firstSidList.getNamedSegmentList().getSegments().getSegment().size(); + createRequest.sids.sids = convertSegmentsToByteArray(firstSidList.getNamedSegmentList().getSegments()); + createRequest.sids.weight = firstSidList.getWeight().intValue(); + + LOG.info("Writing policy {}", createRequest); + getReplyForWrite(getApi().srPolicyAdd(createRequest).toCompletableFuture(), identifier); + + if (segments.size() > 1) { + LOG.info("Multiple segments detected for policy, modifying"); + segments.stream() + .skip(1) + .map(policySegments -> { + SrPolicyMod modifyRequest = new SrPolicyMod(); + modifyRequest.bsidAddr = createRequest.bsidAddr; + modifyRequest.operation = ADD_NEW;// add new segment list + modifyRequest.fibTable = fibTableIndex; + modifyRequest.sids = new Srv6SidList(); + modifyRequest.sids.numSids = + (byte) policySegments.getNamedSegmentList().getSegments().getSegment().size(); + modifyRequest.sids.sids = + convertSegmentsToByteArray(policySegments.getNamedSegmentList().getSegments()); + modifyRequest.sids.weight = policySegments.getWeight().intValue(); + return modifyRequest; + }) + .peek(modifyRequest -> LOG.info("Adding additional segment list for policy {} / request {}", + bindingSidAddress.getValue(), modifyRequest)) + .forEach(modifyRequest -> { + try { + getReplyForWrite(getApi().srPolicyMod(modifyRequest).toCompletableFuture(), identifier); + } catch (WriteFailedException e) { + throw new IllegalStateException(e); + } + }); + } + } + + @Override + public void checkValid() { + checkNotNull(bindingSidAddress, "Binding sid address not set"); + checkNotNull(policyBehavior, "Policy behavior not set"); + checkNotNull(policyType, "Policy type not set"); + if (policyBehavior != SegmentRoutingPolicyBehavior.Encapsulation) { + checkNotNull(segments, "Segments not set"); + checkState(!segments.isEmpty(), "No segments set"); + } + } + + public void setBindingSidAddress( + final Ipv6Address bindingSidAddress) { + this.bindingSidAddress = bindingSidAddress; + } + + public void setPolicyBehavior( + final SegmentRoutingPolicyBehavior policyBehavior) { + this.policyBehavior = policyBehavior; + } + + public void setPolicyType( + final SegmentRoutingPolicyType policyType) { + this.policyType = policyType; + } + + public void setFibTableIndex(final int fibTableIndex) { + this.fibTableIndex = fibTableIndex; + } + + public void setSegments(final List<SidList> segments) { + this.segments = segments; + } +} diff --git a/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/dto/SidList.java b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/dto/SidList.java new file mode 100644 index 000000000..129c20e2d --- /dev/null +++ b/srv6/srv6-impl/src/main/java/io/fd/hc2vpp/srv6/write/policy/request/dto/SidList.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Bell Canada, Pantheon Technologies and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package io.fd.hc2vpp.srv6.write.policy.request.dto; + +import org.opendaylight.yang.gen.v1.http.cisco.com.ns.yang.oc.srte.policy.rev170918.named.segment.lists.named.segment.lists.NamedSegmentList; + +public class SidList { + private Long weight; + private NamedSegmentList namedSegmentList; + + private SidList(final SidListBuilder builder) { + weight = builder.weight; + namedSegmentList = builder.namedSegmentList; + } + + public static SidListBuilder builder() { + return new SidListBuilder(); + } + + public Long getWeight() { + return weight; + } + + public NamedSegmentList getNamedSegmentList() { + return namedSegmentList; + } + + public static final class SidListBuilder { + private Long weight; + private NamedSegmentList namedSegmentList; + + private SidListBuilder() { + } + + public SidListBuilder setWeight(final Long weight) { + this.weight = weight; + return this; + } + + public SidListBuilder setNamedSegmentList(final NamedSegmentList namedSegmentList) { + this.namedSegmentList = namedSegmentList; + return this; + } + + public SidList build() { + return new SidList(this); + } + } +} |