From 708bdc2493788a47605d187302380b38a0db73ec Mon Sep 17 00:00:00 2001 From: Matthias Frei Date: Thu, 27 Jul 2023 16:53:19 +0200 Subject: [PATCH] repo: drop partial colibri implementation (#4372) A partial implementation of colibri was added in a series of PRs (#3446, ..., #3905). This has not been sufficiently completed to become operational, i.e. the colibri implementation in this repository has effectively remained dead code. The colibri prototype was further developed in a fork, github.com/netsec-ethz/scion, and changes have no longer been upstreamed. Consequently the remaining colibri code here has become stale and keeping this is no longer of great use. --- .golangcilint.yml | 10 +- control/colibri/reservation/BUILD.bazel | 28 - control/colibri/reservation/conf/BUILD.bazel | 23 - .../colibri/reservation/conf/capacities.go | 116 --- .../reservation/conf/capacities_test.go | 208 ----- .../reservation/conf/testdata/caps1.json | 26 - .../reservation/conf/testdata/caps2.json | 7 - control/colibri/reservation/e2e/BUILD.bazel | 37 - control/colibri/reservation/e2e/index.go | 44 - control/colibri/reservation/e2e/request.go | 194 ----- .../colibri/reservation/e2e/request_test.go | 220 ----- .../colibri/reservation/e2e/reservation.go | 109 --- .../reservation/e2e/reservation_test.go | 133 --- control/colibri/reservation/e2e/response.go | 82 -- control/colibri/reservation/index.go | 122 --- control/colibri/reservation/index_test.go | 196 ----- control/colibri/reservation/request.go | 45 - .../reservation/reservationdbtest/BUILD.bazel | 18 - .../reservationdbtest/reservationdbtest.go | 704 --------------- .../colibri/reservation/segment/BUILD.bazel | 37 - .../reservation/segment/admission/BUILD.bazel | 9 - .../reservation/segment/admission/admitter.go | 28 - .../segment/admission/impl/BUILD.bazel | 33 - .../segment/admission/impl/stateless.go | 292 ------- .../segment/admission/impl/stateless_test.go | 522 ----------- .../reservation/segment/export_test.go | 26 - control/colibri/reservation/segment/index.go | 75 -- control/colibri/reservation/segment/path.go | 159 ---- .../colibri/reservation/segment/path_test.go | 181 ---- .../colibri/reservation/segment/request.go | 92 -- .../reservation/segment/reservation.go | 233 ----- .../reservation/segment/reservation_test.go | 238 ------ .../colibri/reservation/segment/response.go | 101 --- .../reservation/segmenttest/BUILD.bazel | 13 - .../colibri/reservation/segmenttest/common.go | 50 -- .../colibri/reservation/sqlite/BUILD.bazel | 38 - control/colibri/reservation/sqlite/db.go | 809 ------------------ control/colibri/reservation/sqlite/db_test.go | 135 --- control/colibri/reservation/sqlite/schema.go | 103 --- control/colibri/reservation/test/BUILD.bazel | 9 - control/colibri/reservation/test/path.go | 57 -- control/colibri/reservation/types.go | 42 - .../colibri/reservationstorage/BUILD.bazel | 14 - .../reservationstorage/backend/BUILD.bazel | 15 - .../colibri/reservationstorage/backend/db.go | 95 -- .../backend/mock_backend/BUILD.bazel | 28 - .../backend/mock_backend/mock.go | 498 ----------- control/colibri/reservationstorage/store.go | 51 -- control/colibri/reservationstore/BUILD.bazel | 25 - control/colibri/reservationstore/store.go | 594 ------------- .../colibri/reservationstore/store_test.go | 26 - doc/dev/design/ColibriService.md | 11 +- doc/dev/design/index.rst | 2 +- nogo.json | 1 - .../colibri/reservation/BUILD.bazel | 23 - pkg/experimental/colibri/reservation/types.go | 584 ------------- .../colibri/reservation/types_test.go | 515 ----------- tools/topogen.py | 2 - tools/topology/common.py | 2 - tools/topology/config.py | 1 - tools/topology/go.py | 95 -- tools/topology/prometheus.py | 1 - tools/topology/topo.py | 7 - 63 files changed, 13 insertions(+), 8181 deletions(-) delete mode 100644 control/colibri/reservation/BUILD.bazel delete mode 100644 control/colibri/reservation/conf/BUILD.bazel delete mode 100644 control/colibri/reservation/conf/capacities.go delete mode 100644 control/colibri/reservation/conf/capacities_test.go delete mode 100644 control/colibri/reservation/conf/testdata/caps1.json delete mode 100644 control/colibri/reservation/conf/testdata/caps2.json delete mode 100644 control/colibri/reservation/e2e/BUILD.bazel delete mode 100644 control/colibri/reservation/e2e/index.go delete mode 100644 control/colibri/reservation/e2e/request.go delete mode 100644 control/colibri/reservation/e2e/request_test.go delete mode 100644 control/colibri/reservation/e2e/reservation.go delete mode 100644 control/colibri/reservation/e2e/reservation_test.go delete mode 100644 control/colibri/reservation/e2e/response.go delete mode 100644 control/colibri/reservation/index.go delete mode 100644 control/colibri/reservation/index_test.go delete mode 100644 control/colibri/reservation/request.go delete mode 100644 control/colibri/reservation/reservationdbtest/BUILD.bazel delete mode 100644 control/colibri/reservation/reservationdbtest/reservationdbtest.go delete mode 100644 control/colibri/reservation/segment/BUILD.bazel delete mode 100644 control/colibri/reservation/segment/admission/BUILD.bazel delete mode 100644 control/colibri/reservation/segment/admission/admitter.go delete mode 100644 control/colibri/reservation/segment/admission/impl/BUILD.bazel delete mode 100644 control/colibri/reservation/segment/admission/impl/stateless.go delete mode 100644 control/colibri/reservation/segment/admission/impl/stateless_test.go delete mode 100644 control/colibri/reservation/segment/export_test.go delete mode 100644 control/colibri/reservation/segment/index.go delete mode 100644 control/colibri/reservation/segment/path.go delete mode 100644 control/colibri/reservation/segment/path_test.go delete mode 100644 control/colibri/reservation/segment/request.go delete mode 100644 control/colibri/reservation/segment/reservation.go delete mode 100644 control/colibri/reservation/segment/reservation_test.go delete mode 100644 control/colibri/reservation/segment/response.go delete mode 100644 control/colibri/reservation/segmenttest/BUILD.bazel delete mode 100644 control/colibri/reservation/segmenttest/common.go delete mode 100644 control/colibri/reservation/sqlite/BUILD.bazel delete mode 100644 control/colibri/reservation/sqlite/db.go delete mode 100644 control/colibri/reservation/sqlite/db_test.go delete mode 100644 control/colibri/reservation/sqlite/schema.go delete mode 100644 control/colibri/reservation/test/BUILD.bazel delete mode 100644 control/colibri/reservation/test/path.go delete mode 100644 control/colibri/reservation/types.go delete mode 100644 control/colibri/reservationstorage/BUILD.bazel delete mode 100644 control/colibri/reservationstorage/backend/BUILD.bazel delete mode 100644 control/colibri/reservationstorage/backend/db.go delete mode 100644 control/colibri/reservationstorage/backend/mock_backend/BUILD.bazel delete mode 100644 control/colibri/reservationstorage/backend/mock_backend/mock.go delete mode 100644 control/colibri/reservationstorage/store.go delete mode 100644 control/colibri/reservationstore/BUILD.bazel delete mode 100644 control/colibri/reservationstore/store.go delete mode 100644 control/colibri/reservationstore/store_test.go delete mode 100644 pkg/experimental/colibri/reservation/BUILD.bazel delete mode 100644 pkg/experimental/colibri/reservation/types.go delete mode 100644 pkg/experimental/colibri/reservation/types_test.go diff --git a/.golangcilint.yml b/.golangcilint.yml index 3bbbf5eaa3..1b79a32408 100644 --- a/.golangcilint.yml +++ b/.golangcilint.yml @@ -73,14 +73,8 @@ issues: - path: pkg/scrypto/cms linters: [goheader] - # Exceptions to errcheck for some experimental colibri code and some old-ish convey tests. + # Exceptions to errcheck for some old-ish convey tests. - linters: [errcheck] - path: "^control/colibri/reservation/e2e/reservation_test.go$|\ - ^control/colibri/reservation/index_test.go$|\ - ^control/colibri/reservation/reservationdbtest/reservationdbtest.go$|\ - ^control/colibri/reservation/segment/reservation_test.go$|\ - ^control/colibri/reservation/sqlite/db_test.go$|\ - ^control/colibri/reservationstore/store.go$|\ - ^pkg/sock/reliable/reconnect/conn_io_test.go$|\ + path: "^pkg/sock/reliable/reconnect/conn_io_test.go$|\ ^pkg/sock/reliable/reconnect/network_test.go$|\ ^pkg/sock/reliable/reconnect/reconnecter_test.go$" diff --git a/control/colibri/reservation/BUILD.bazel b/control/colibri/reservation/BUILD.bazel deleted file mode 100644 index e4da7ae3e6..0000000000 --- a/control/colibri/reservation/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "index.go", - "request.go", - "types.go", - ], - importpath = "github.com/scionproto/scion/control/colibri/reservation", - visibility = ["//visibility:public"], - deps = [ - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - "//pkg/private/util:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["index_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/util:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/conf/BUILD.bazel b/control/colibri/reservation/conf/BUILD.bazel deleted file mode 100644 index ff2704e92b..0000000000 --- a/control/colibri/reservation/conf/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["capacities.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/conf", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["capacities_test.go"], - data = glob(["testdata/**"]), - embed = [":go_default_library"], - deps = [ - "//pkg/private/xtest:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/conf/capacities.go b/control/colibri/reservation/conf/capacities.go deleted file mode 100644 index d057a31db3..0000000000 --- a/control/colibri/reservation/conf/capacities.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 conf - -import ( - "encoding/json" - "sort" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// internal structure used to serialize to and from json. -type capacities struct { - // ingress capacities - CapIn map[uint16]uint64 `json:"ingress_kbps"` - // egress capacities - CapEg map[uint16]uint64 `json:"egress_kbps"` - // configured allowed transit - In2Eg map[uint16]map[uint16]uint64 `json:"ingress_to_egress_kbps"` -} - -// Capacities aka capacity matrix. -type Capacities struct { - c capacities - // derived fields from the above ones: - inIfs []uint16 - egIfs []uint16 -} - -var _ base.Capacities = (*Capacities)(nil) -var _ json.Unmarshaler = (*Capacities)(nil) -var _ json.Marshaler = (*Capacities)(nil) - -func (c *Capacities) IngressInterfaces() []uint16 { return c.inIfs } -func (c *Capacities) EgressInterfaces() []uint16 { return c.egIfs } -func (c *Capacities) Capacity(from, to uint16) uint64 { return c.c.In2Eg[from][to] } -func (c *Capacities) CapacityIngress(ingress uint16) uint64 { return c.c.CapIn[ingress] } -func (c *Capacities) CapacityEgress(egress uint16) uint64 { return c.c.CapEg[egress] } - -// UnmarshalJSON deserializes into the json-aware internal data structure. -func (c *Capacities) UnmarshalJSON(b []byte) error { - if err := json.Unmarshal(b, &c.c); err != nil { - return err - } - return c.init() -} - -// MarshalJSON serializes the internal json-friendly structure. -func (c Capacities) MarshalJSON() ([]byte, error) { - return json.Marshal(c.c) -} - -func (c *Capacities) init() error { - totalEgress := make(map[uint16]uint64) - for ingress, intoMap := range c.c.In2Eg { - var accumIngress uint64 - for egress, cap := range intoMap { - accumIngress += cap - totalEgress[egress] += cap - if egress == ingress && cap != 0 { - return serrors.New("capacity is inconsistent, ingress to itself not zero", - "ingress", ingress) - } - } - if _, found := c.c.CapIn[ingress]; !found { - return serrors.New("capacity is inconsistent, must declare ingress capacity", - "ingress", ingress) - } - if accumIngress > c.c.CapIn[ingress] { - return serrors.New("capacity is inconsistent, ingress accum too high", "ingress", - ingress, "ingress_accum", accumIngress, "ingress_declared", c.c.CapIn[ingress]) - } - } - for egress, accum := range totalEgress { - if _, found := c.c.CapEg[egress]; !found { - return serrors.New("capacity is inconsistent, must declare egress capacity", - "egress", egress) - } - if accum > c.c.CapEg[egress] { - return serrors.New("capacity is inconsistent, egress accum too high", "egress", egress, - "egress_accum", accum, "egress_declared", c.c.CapEg[egress]) - } - } - // init list of ingress interfaces - c.inIfs = make([]uint16, len(c.c.CapIn)) - i := 0 - for ifid := range c.c.CapIn { - c.inIfs[i] = ifid - i++ - } - // init list of egress interfaces - c.egIfs = make([]uint16, len(c.c.CapEg)) - i = 0 - for ifid := range c.c.CapEg { - c.egIfs[i] = ifid - i++ - } - // sort them just to simplify debugging - sort.Slice(c.inIfs, func(i, j int) bool { return c.inIfs[i] < c.inIfs[j] }) - sort.Slice(c.egIfs, func(i, j int) bool { return c.egIfs[i] < c.egIfs[j] }) - - return nil -} diff --git a/control/colibri/reservation/conf/capacities_test.go b/control/colibri/reservation/conf/capacities_test.go deleted file mode 100644 index 4818619765..0000000000 --- a/control/colibri/reservation/conf/capacities_test.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 conf - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestJson(t *testing.T) { - // test that this number cannot be represented with float64 - // (it will be used in one of the test cases) - var bigint uint64 = 4418489049307132905 - require.NotEqual(t, bigint, uint64(float64(bigint))) - - cases := map[string]struct { - filename string - cap Capacities - }{ - "using all keys": { - filename: "caps1.json", - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 200, 3: 300}, - CapEg: map[uint16]uint64{1: 100, 2: 200, 3: 40}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 10, 3: 20}, - 2: {1: 10, 3: 20}, - 3: {1: 10, 2: 20}, - }, - }, - }, - }, - "big num no float": { - filename: "caps2.json", - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: bigint}, - }, - }, - }, - } - - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - require.NoError(t, tc.cap.init()) - buf, err := json.MarshalIndent(tc.cap, "", " ") - buf = append(buf, '\n') - require.NoError(t, err) - expectedJSON := xtest.MustReadFromFile(t, tc.filename) - require.Equal(t, expectedJSON, buf) - c2 := Capacities{} - err = c2.UnmarshalJSON(expectedJSON) - require.NoError(t, err) - require.Equal(t, tc.cap, c2) - }) - } -} - -func TestValidation(t *testing.T) { - cases := map[string]struct { - okay bool - cap Capacities - }{ - "okay": { - okay: true, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 200, 3: 300}, - CapEg: map[uint16]uint64{1: 100, 2: 200, 3: 40}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 10, 3: 20}, - 2: {1: 10, 3: 20}, - 3: {1: 10, 2: 20}, - }, - }, - }, - }, - "too much ingress": { - okay: false, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 10}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 10, 3: 1}, - }, - }, - }, - }, - "too much egress": { - okay: false, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - CapEg: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 90, 3: 1}, - 3: {2: 20, 1: 1}, - }, - }, - }, - }, - "ingress to itself (reflection not allowed)": { - okay: false, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - CapEg: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {1: 10, 3: 1}, - }, - }, - }, - }, - "too few ingress capacities": { - okay: false, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 3: 100}, - CapEg: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 0, 3: 0}, - 2: {1: 0, 3: 0}, - 3: {1: 0, 2: 0}, - }, - }, - }, - }, - "too few egress capacities": { - okay: false, - cap: Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - CapEg: map[uint16]uint64{1: 100, 3: 100}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 0, 3: 0}, - 2: {1: 0, 3: 0}, - 3: {1: 0, 2: 0}, - }, - }, - }, - }, - } - - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - err := tc.cap.init() - if tc.okay { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} - -func TestCapacities(t *testing.T) { - c := &Capacities{c: capacities{ - CapIn: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - CapEg: map[uint16]uint64{1: 100, 2: 100, 3: 100}, - In2Eg: map[uint16]map[uint16]uint64{ - 1: {2: 12, 3: 13}, - 2: {1: 21, 3: 23}, - 3: {1: 31, 2: 32}, - }, - }, - } - copy := cloneCapacities(c) // save a copy of the original - require.Equal(t, uint64(100), c.CapacityIngress(1)) - require.Equal(t, uint64(0), c.CapacityIngress(5)) - - require.Equal(t, copy, c) -} - -func cloneCapacities(c *Capacities) *Capacities { - ret := &Capacities{} - copy(ret.inIfs, c.inIfs) - copy(ret.egIfs, c.egIfs) - ret.c.CapIn = make(map[uint16]uint64) - for k, v := range c.c.CapIn { - ret.c.CapIn[k] = v - } - ret.c.CapEg = make(map[uint16]uint64) - for k, v := range c.c.CapEg { - ret.c.CapEg[k] = v - } - ret.c.In2Eg = make(map[uint16]map[uint16]uint64) - for k, v := range c.c.In2Eg { - m := make(map[uint16]uint64) - for k, v := range v { - m[k] = v - } - ret.c.In2Eg[k] = m - } - return ret -} diff --git a/control/colibri/reservation/conf/testdata/caps1.json b/control/colibri/reservation/conf/testdata/caps1.json deleted file mode 100644 index 0fc0bdcf60..0000000000 --- a/control/colibri/reservation/conf/testdata/caps1.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "ingress_kbps": { - "1": 100, - "2": 200, - "3": 300 - }, - "egress_kbps": { - "1": 100, - "2": 200, - "3": 40 - }, - "ingress_to_egress_kbps": { - "1": { - "2": 10, - "3": 20 - }, - "2": { - "1": 10, - "3": 20 - }, - "3": { - "1": 10, - "2": 20 - } - } -} diff --git a/control/colibri/reservation/conf/testdata/caps2.json b/control/colibri/reservation/conf/testdata/caps2.json deleted file mode 100644 index a4ec915fd2..0000000000 --- a/control/colibri/reservation/conf/testdata/caps2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ingress_kbps": { - "1": 4418489049307132905 - }, - "egress_kbps": null, - "ingress_to_egress_kbps": null -} diff --git a/control/colibri/reservation/e2e/BUILD.bazel b/control/colibri/reservation/e2e/BUILD.bazel deleted file mode 100644 index c56f5fb092..0000000000 --- a/control/colibri/reservation/e2e/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "index.go", - "request.go", - "reservation.go", - "response.go", - ], - importpath = "github.com/scionproto/scion/control/colibri/reservation/e2e", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = [ - "request_test.go", - "reservation_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservation/segmenttest:go_default_library", - "//control/colibri/reservation/test:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/util:go_default_library", - "//pkg/private/xtest:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/e2e/index.go b/control/colibri/reservation/e2e/index.go deleted file mode 100644 index f704ce6b14..0000000000 --- a/control/colibri/reservation/e2e/index.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" -) - -// Index represents an E2E index. These are interpreted as "active", so the reservation initiator -// must cleanup indices along the path when the setup didn't finish correctly. -type Index struct { - Idx reservation.IndexNumber - Expiration time.Time - AllocBW reservation.BWCls // also present in the token - Token *reservation.Token -} - -type Indices []Index - -var _ base.IndicesInterface = (*Indices)(nil) - -func (idxs Indices) Len() int { return len(idxs) } -func (idxs Indices) GetIndexNumber(i int) reservation.IndexNumber { return idxs[i].Idx } -func (idxs Indices) GetExpiration(i int) time.Time { return idxs[i].Expiration } -func (idxs Indices) GetAllocBW(i int) reservation.BWCls { return idxs[i].AllocBW } -func (idxs Indices) GetToken(i int) *reservation.Token { return idxs[i].Token } -func (idxs Indices) Rotate(i int) base.IndicesInterface { - return append(idxs[i:], idxs[:i]...) -} diff --git a/control/colibri/reservation/e2e/request.go b/control/colibri/reservation/e2e/request.go deleted file mode 100644 index a8fb847544..0000000000 --- a/control/colibri/reservation/e2e/request.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Request is the base struct for any type of COLIBRI e2e request. -type Request struct { - base.RequestMetadata // information about the request (forwarding path) - ID reservation.E2EID // the ID this request refers to - Index reservation.IndexNumber // the index this request refers to - Timestamp time.Time // the mandatory timestamp -} - -// NewRequest constructs the e2e Request type. -func NewRequest(ts time.Time, id *reservation.E2EID, idx reservation.IndexNumber, - path base.ColibriPath) (*Request, error) { - - metadata, err := base.NewRequestMetadata(path) - if err != nil { - return nil, serrors.WrapStr("new segment request", err) - } - - if id == nil { - return nil, serrors.New("new e2e request with nil ID") - } - - return &Request{ - RequestMetadata: *metadata, - ID: *id, - Index: idx, - Timestamp: ts, - }, nil -} - -// SetupRequest represents all possible e2e setup requests. -type SetupRequest interface { - IsSuccessful() bool - GetCommonSetupReq() *SetupReq // return the underlying basic SetupReq (common for all) -} - -// SetupReq is an e2e setup/renewal request, that has been so far accepted. -type SetupReq struct { - Request - SegmentRsvs []reservation.SegmentID - SegmentRsvASCount []uint8 // how many ASes per segment reservation - RequestedBW reservation.BWCls - AllocationTrail []reservation.BWCls - totalASCount int - currentASSegmentRsvIndex int // the index in SegmentRsv for the current AS - isTransfer bool -} - -// NewSetupRequest creates and initializes an e2e setup request common for both success and failure. -func NewSetupRequest(r *Request, segRsvs []reservation.SegmentID, segRsvCount []uint8, - requestedBW reservation.BWCls, allocTrail []reservation.BWCls) (*SetupReq, error) { - - if len(segRsvs) != len(segRsvCount) || len(segRsvs) == 0 { - return nil, serrors.New("e2e setup request invalid", "seg_rsv_len", len(segRsvs), - "seg_rsv_count_len", len(segRsvCount)) - } - totalASCount := 0 - currASindex := -1 - isTransfer := false - n := len(allocTrail) - 1 - for i, c := range segRsvCount { - totalASCount += int(c) - n -= int(c) - 1 - if i == len(segRsvCount)-1 { - n-- // the last segment spans 1 more AS - } - if n < 0 && currASindex < 0 { - currASindex = i - isTransfer = i < len(segRsvCount)-1 && n == -1 // dst AS is no transfer - } - } - totalASCount -= len(segRsvCount) - 1 - if currASindex < 0 { - return nil, serrors.New("error initializing e2e request", - "alloc_trail_len", len(allocTrail), "seg_rsv_count", segRsvCount) - } - return &SetupReq{ - Request: *r, - SegmentRsvs: segRsvs, - SegmentRsvASCount: segRsvCount, - RequestedBW: requestedBW, - AllocationTrail: allocTrail, - totalASCount: totalASCount, - currentASSegmentRsvIndex: currASindex, - isTransfer: isTransfer, - }, nil -} - -// GetCommonSetupReq returns the pointer to the data structure. -func (r *SetupReq) GetCommonSetupReq() *SetupReq { - return r -} - -func (r *SetupReq) Transfer() bool { - return r.isTransfer -} - -type PathLocation int - -const ( - Source PathLocation = iota - Transit - Destination -) - -func (l PathLocation) String() string { - switch l { - case Source: - return "source" - case Transit: - return "trantit" - case Destination: - return "destination" - } - return "unknown path location" -} - -// Location returns the location of this node in the path of the request. -func (r *SetupReq) Location() PathLocation { - switch len(r.AllocationTrail) { - case 0: - return Source - case r.totalASCount: - return Destination - default: - return Transit - } -} - -// SegmentRsvIDsForThisAS returns the segment reservation ID this AS belongs to. Iff this -// AS is a transfer AS (stitching point), there will be two reservation IDs returned, in the -// order of traversal. -func (r *SetupReq) SegmentRsvIDsForThisAS() []reservation.SegmentID { - indices := make([]reservation.SegmentID, 1, 2) - indices[0] = r.SegmentRsvs[r.currentASSegmentRsvIndex] - if r.isTransfer { - indices = append(indices, r.SegmentRsvs[r.currentASSegmentRsvIndex+1]) - } - return indices -} - -// SetupReqSuccess is a successful e2e setup request traveling along the reservation path. -type SetupReqSuccess struct { - SetupReq - Token reservation.Token -} - -var _ SetupRequest = (*SetupReqSuccess)(nil) - -// IsSuccessful returns true. -func (s *SetupReqSuccess) IsSuccessful() bool { - return true -} - -// SetupReqFailure is a failed e2e setup request also traveling along the reservation path. -type SetupReqFailure struct { - SetupReq - ErrorCode uint8 -} - -var _ SetupRequest = (*SetupReqFailure)(nil) - -// IsSuccessful returns false, as this is a failed setup. -func (s *SetupReqFailure) IsSuccessful() bool { - return false -} - -// CleanupReq is a cleaup request for an e2e index. -type CleanupReq struct { - Request -} diff --git a/control/colibri/reservation/e2e/request_test.go b/control/colibri/reservation/e2e/request_test.go deleted file mode 100644 index 25b0150ee5..0000000000 --- a/control/colibri/reservation/e2e/request_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/test" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestNewRequest(t *testing.T) { - _, err := NewRequest(util.SecsToTime(1), nil, 1, nil) - require.Error(t, err) - _, err = NewRequest(util.SecsToTime(1), nil, 1, test.NewTestPath()) - require.Error(t, err) - id, err := reservation.NewE2EID(xtest.MustParseAS("ff00:0:111"), - xtest.MustParseHexString("beefcafebeefcafebeef")) - require.NoError(t, err) - r, err := NewRequest(util.SecsToTime(1), id, 1, test.NewTestPath()) - require.NoError(t, err) - require.Equal(t, util.SecsToTime(1), r.Timestamp) - require.Equal(t, id, &r.ID) - require.Equal(t, reservation.IndexNumber(1), r.Index) - require.Equal(t, test.NewTestPath(), r.RequestMetadata.Path()) -} - -func TestNewSetupRequest(t *testing.T) { - _, err := NewSetupRequest(nil, nil, nil, 5, nil) - require.Error(t, err) - id, err := reservation.NewE2EID(xtest.MustParseAS("ff00:0:111"), - xtest.MustParseHexString("beefcafebeefcafebeef")) - require.NoError(t, err) - path := test.NewTestPath() - baseReq, err := NewRequest(util.SecsToTime(1), id, 1, path) - require.NoError(t, err) - _, err = NewSetupRequest(baseReq, nil, nil, 5, nil) - require.Error(t, err) - - segmentRsvs := make([]reservation.SegmentID, 0) - _, err = NewSetupRequest(baseReq, segmentRsvs, nil, 5, nil) - require.Error(t, err) - segmentASCount := make([]uint8, 0) - _, err = NewSetupRequest(baseReq, segmentRsvs, segmentASCount, 5, nil) - require.Error(t, err) - trail := make([]reservation.BWCls, 0) - _, err = NewSetupRequest(baseReq, segmentRsvs, segmentASCount, 5, trail) - require.Error(t, err) - - cases := map[string]struct { - ASCountPerSegment []uint8 - TrailLength int - TotalASCount int - SegmentIndex int - PathLocation PathLocation - IsTransfer bool - }{ - // "3-2-4 at 0" means: - // 3 segments, with 3 ASes in the first one, 2 and 4 in the others. Trail has 0 components - "2 at 0": { - ASCountPerSegment: []uint8{2}, - TrailLength: 0, - TotalASCount: 2, - SegmentIndex: 0, - PathLocation: Source, - IsTransfer: false, - }, - "2 at 1": { - ASCountPerSegment: []uint8{2}, - TrailLength: 1, - TotalASCount: 2, - SegmentIndex: 0, - PathLocation: Transit, - IsTransfer: false, - }, - "2 at 2": { - ASCountPerSegment: []uint8{2}, - TrailLength: 2, - TotalASCount: 2, - SegmentIndex: 0, - PathLocation: Destination, - IsTransfer: false, - }, - "3-4-5 at 0": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 0, - TotalASCount: 10, - SegmentIndex: 0, - PathLocation: Source, - IsTransfer: false, - }, - "3-4-5 at 1": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 1, - TotalASCount: 10, - SegmentIndex: 0, - PathLocation: Transit, - IsTransfer: false, - }, - "3-4-5 at 2": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 2, - TotalASCount: 10, - SegmentIndex: 0, - PathLocation: Transit, - IsTransfer: true, - }, - "3-4-5 at 3": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 3, - TotalASCount: 10, - SegmentIndex: 1, - PathLocation: Transit, - IsTransfer: false, - }, - "3-4-5 at 5": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 5, - TotalASCount: 10, - SegmentIndex: 1, - PathLocation: Transit, - IsTransfer: true, - }, - "3-4-5 at 6": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 6, - TotalASCount: 10, - SegmentIndex: 2, - PathLocation: Transit, - IsTransfer: false, - }, - "3-4-5 at 9": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 9, - TotalASCount: 10, - SegmentIndex: 2, - PathLocation: Transit, - IsTransfer: false, - }, - "3-4-5 at 10": { - ASCountPerSegment: []uint8{3, 4, 5}, - TrailLength: 10, - TotalASCount: 10, - SegmentIndex: 2, - PathLocation: Destination, - IsTransfer: false, - }, - } - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - segmentRsvs := make([]reservation.SegmentID, len(tc.ASCountPerSegment)) - for i := range segmentRsvs { - segmentRsvs[i] = *newTestSegmentID(t) - } - trail := make([]reservation.BWCls, tc.TrailLength) - for i := range trail { - trail[i] = 5 - } - r, err := NewSetupRequest(baseReq, segmentRsvs, tc.ASCountPerSegment, 5, trail) - require.NoError(t, err) - require.Equal(t, tc.TotalASCount, r.totalASCount) - require.Equal(t, tc.SegmentIndex, r.currentASSegmentRsvIndex) - require.Equal(t, tc.PathLocation, r.Location()) - require.Equal(t, tc.IsTransfer, r.Transfer()) - }) - } -} - -func TestInterface(t *testing.T) { - id, err := reservation.NewE2EID(xtest.MustParseAS("ff00:0:111"), - xtest.MustParseHexString("beefcafebeefcafebeef")) - require.NoError(t, err) - path := test.NewTestPath() - baseReq, err := NewRequest(util.SecsToTime(1), id, 1, path) - require.NoError(t, err) - segmentIDs := []reservation.SegmentID{*newTestSegmentID(t)} - - r, err := NewSetupRequest(baseReq, segmentIDs, []uint8{2}, 5, nil) - require.NoError(t, err) - tok, err := reservation.TokenFromRaw(xtest.MustParseHexString( - "16ebdb4f0d042500003f001002bad1ce003f001002facade")) - require.NoError(t, err) - success := SetupReqSuccess{ - SetupReq: *r, - Token: *tok, - } - require.Equal(t, r, success.GetCommonSetupReq()) - failure := SetupReqFailure{ - SetupReq: *r, - ErrorCode: 6, - } - require.Equal(t, r, failure.GetCommonSetupReq()) -} - -// this fcn is helpful here to add segment reservations in the e2e setup request. -func newTestSegmentID(t *testing.T) *reservation.SegmentID { - t.Helper() - id, err := reservation.NewSegmentID(xtest.MustParseAS("ff00:0:1"), - xtest.MustParseHexString("deadbeef")) - require.NoError(t, err) - return id -} diff --git a/control/colibri/reservation/e2e/reservation.go b/control/colibri/reservation/e2e/reservation.go deleted file mode 100644 index 5d84afd7a5..0000000000 --- a/control/colibri/reservation/e2e/reservation.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Reservation represents an E2E reservation. -type Reservation struct { - ID reservation.E2EID - SegmentReservations []*segment.Reservation // stitched segment reservations - Indices Indices -} - -// Validate will return an error for invalid values. -// It doesn not check for valid path properties and correct end/start AS ID when stiching. -func (r *Reservation) Validate() error { - if err := base.ValidateIndices(r.Indices); err != nil { - return err - } - if len(r.SegmentReservations) < 1 || len(r.SegmentReservations) > 3 { - return serrors.New("wrong number of segment reservations referenced in E2E reservation", - "number", len(r.SegmentReservations)) - } - for i, rsv := range r.SegmentReservations { - if rsv == nil { - return serrors.New("invalid segment reservation referenced by e2e, is nil", - "slice_index", i) - } - if err := rsv.Validate(); err != nil { - return serrors.New("invalid segment reservation referenced by e2e one", - "slice_index", i, "segment_id", rsv.ID) - } - } - return nil -} - -// NewIndex creates a new index in this reservation. The token needs to be created manually. -func (r *Reservation) NewIndex(expTime time.Time) (reservation.IndexNumber, error) { - idx := reservation.IndexNumber(0) - if len(r.Indices) > 0 { - idx = r.Indices[len(r.Indices)-1].Idx.Add(1) - } - newIndices := make(Indices, len(r.Indices)+1) - copy(newIndices, r.Indices) - newIndices[len(newIndices)-1] = Index{ - Expiration: expTime, - Idx: idx, - } - if err := base.ValidateIndices(newIndices); err != nil { - return 0, err - } - r.Indices = newIndices - return idx, nil -} - -// RemoveIndex removes all indices from the beginning until this one, inclusive. -func (r *Reservation) RemoveIndex(idx reservation.IndexNumber) error { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return err - } - r.Indices = r.Indices[sliceIndex+1:] - return nil -} - -// Index finds the Index with that IndexNumber and returns a pointer to it. Nil if not found. -func (r *Reservation) Index(idx reservation.IndexNumber) *Index { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return nil - } - return &r.Indices[sliceIndex] -} - -// AllocResv returns the allocated bandwidth by this reservation using the current active index and -// the previous one. The max of those two values is used because the current active index might -// be rolled back with a cleanup request. The return units is Kbps. -func (r *Reservation) AllocResv() uint64 { - var maxBW reservation.BWCls - switch len(r.Indices) { - case 0: - return 0 - case 1: - maxBW = r.Indices[len(r.Indices)-1].AllocBW - default: - maxBW = reservation.MaxBWCls(r.Indices[len(r.Indices)-1].AllocBW, - r.Indices[len(r.Indices)-2].AllocBW) - } - return maxBW.ToKbps() -} diff --git a/control/colibri/reservation/e2e/reservation_test.go b/control/colibri/reservation/e2e/reservation_test.go deleted file mode 100644 index 7a3a9a20eb..0000000000 --- a/control/colibri/reservation/e2e/reservation_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segmenttest" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestValidate(t *testing.T) { - // alright - r := newReservation() - err := r.Validate() - require.NoError(t, err) - - // no segment reservations - r = newReservation() - r.SegmentReservations = make([]*segment.Reservation, 0) - err = r.Validate() - require.Error(t, err) - - // nil segment reservation - r = newReservation() - r.SegmentReservations[0] = nil - err = r.Validate() - require.Error(t, err) - - // invalid segment reservation - r = newReservation() - r.SegmentReservations[0].Path = segment.ReservationTransparentPath{} - err = r.Validate() - require.Error(t, err) - - // more than 3 segment reservations - r = newReservation() - r.SegmentReservations = []*segment.Reservation{ - newSegmentReservation("1-ff00:0:111", "1-ff00:0:110"), - newSegmentReservation("1-ff00:0:111", "1-ff00:0:110"), - newSegmentReservation("1-ff00:0:111", "1-ff00:0:110"), - newSegmentReservation("1-ff00:0:111", "1-ff00:0:110"), - } - err = r.Validate() - require.Error(t, err) -} - -func TestNewIndex(t *testing.T) { - r := newReservation() - expTime := util.SecsToTime(1) - index, err := r.NewIndex(expTime) - require.NoError(t, err) - require.Len(t, r.Indices, 1) - require.Equal(t, r.Indices[0].Idx, index) - index, err = r.NewIndex(expTime) - require.NoError(t, err) - require.Len(t, r.Indices, 2) - require.Equal(t, r.Indices[1].Idx, index) -} - -func TestRemoveIndex(t *testing.T) { - r := newReservation() - expTime := util.SecsToTime(1) - idx, _ := r.NewIndex(expTime) - err := r.RemoveIndex(idx) - require.NoError(t, err) - require.Len(t, r.Indices, 0) -} - -func TestAllocResv(t *testing.T) { - r := newReservation() - // 1 index - r.NewIndex(util.SecsToTime(1)) - r.Index(0).AllocBW = 5 - require.Equal(t, uint64(64), r.AllocResv()) - // 2 indices - r.NewIndex(util.SecsToTime(2)) - r.Index(1).AllocBW = 3 - require.Equal(t, uint64(64), r.AllocResv()) - // 3 indices - r.NewIndex(util.SecsToTime(2)) - r.Index(2).AllocBW = 3 - require.Equal(t, uint64(32), r.AllocResv()) -} - -func newSegmentReservation(asidPath ...string) *segment.Reservation { - if len(asidPath) < 2 { - panic("at least source and destination in the path") - } - r := segmenttest.NewReservation() - // use the asid to create an ID and a path and use them in the reservation - pathComponents := make([]interface{}, len(asidPath)*3) - for i := range asidPath { - pathComponents[i*3] = i * 2 - pathComponents[i*3+1] = asidPath[i] - pathComponents[i*3+2] = i*2 + 1 - } - pathComponents[len(pathComponents)-1] = 0 - r.Path = segmenttest.NewPathFromComponents(pathComponents...) - return r -} - -func newReservation() *Reservation { - id, err := reservation.NewE2EID(xtest.MustParseAS("ff00:0:111"), - xtest.MustParseHexString("beefcafebeefcafebeef")) - if err != nil { - panic(err) - } - rsv := Reservation{ - ID: *id, - SegmentReservations: []*segment.Reservation{ - newSegmentReservation("1-ff00:0:111", "1-ff00:0:110"), - }, - } - return &rsv -} diff --git a/control/colibri/reservation/e2e/response.go b/control/colibri/reservation/e2e/response.go deleted file mode 100644 index 6d294be865..0000000000 --- a/control/colibri/reservation/e2e/response.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 e2e - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Response is the base struct for any type of COLIBRI e2e response. -type Response struct { - base.RequestMetadata // information about the request (forwarding path) - ID reservation.E2EID // the ID this request refers to - Index reservation.IndexNumber // the index this request refers to - Accepted bool // success or failure type of response - FailedHop uint8 // if accepted is false, the AS that failed it -} - -// NewResponse contructs the segment Response type. -func NewResponse(ts time.Time, id *reservation.E2EID, idx reservation.IndexNumber, - path base.ColibriPath, accepted bool, failedHop uint8) (*Response, error) { - - metadata, err := base.NewRequestMetadata(path) - if err != nil { - return nil, serrors.WrapStr("new segment request", err) - } - if id == nil { - return nil, serrors.New("new segment response with nil ID") - } - return &Response{ - RequestMetadata: *metadata, - ID: *id, - Index: idx, - Accepted: accepted, - FailedHop: failedHop, - }, nil -} - -// IsHopByHop returns false, as all the responses travel directly to the source endhost. -func (r *Response) IsHopByHop() bool { - return false -} - -// ResponseSetupSuccess is the response to a success setup. It's sent on the reverse direction. -type ResponseSetupSuccess struct { - Response - Token reservation.Token -} - -// ResponseSetupFailure is the response to a failed setup. It's sent on the reverse direction. -// The failed hop is the length of MaxBWs + 1. -type ResponseSetupFailure struct { - Response - ErrorCode uint8 - MaxBWs []reservation.BWCls // granted by ASes in the path until the failed hop -} - -// ResponseCleanupSuccess is a response to a successful cleanup request. -type ResponseCleanupSuccess struct { - Response -} - -// ResponseCleanupFailure is a failed index cleanup. -type ResponseCleanupFailure struct { - Response - ErrorCode uint8 -} diff --git a/control/colibri/reservation/index.go b/control/colibri/reservation/index.go deleted file mode 100644 index 3cff23cd4a..0000000000 --- a/control/colibri/reservation/index.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservation - -import ( - "sort" - "time" - - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" - "github.com/scionproto/scion/pkg/private/util" -) - -type IndicesInterface interface { - Len() int - GetIndexNumber(i int) reservation.IndexNumber - GetExpiration(i int) time.Time - GetAllocBW(i int) reservation.BWCls - GetToken(i int) *reservation.Token - Rotate(i int) IndicesInterface -} - -// ValidateIndices checks that the indices follow consecutive index numbers, their expiration -// times are greater or equal, and no more than three indices per expiration time. Also no more -// than 16 indices are allowed. -func ValidateIndices(indices IndicesInterface) error { - lastExpiration := util.SecsToTime(0) - lastIndexNumber := reservation.IndexNumber(0).Sub(1) - if indices.Len() > 0 { - lastIndexNumber = indices.GetIndexNumber(0).Sub(1) - if indices.Len() > 16 { - // with only 4 bits to represent the index number, we cannot have more than 16 indices - return serrors.New("too many indices", "index_count", indices.Len()) - } - } - indicesPerExpTime := 0 - for i := 0; i < indices.Len(); i++ { - if indices.GetExpiration(i).Before(lastExpiration) { - return serrors.New("index expires before than a previous one", - "idx", indices.GetIndexNumber(i), - "expiration", indices.GetExpiration(i), "previous_exp", lastExpiration) - } - if indices.GetExpiration(i).Equal(lastExpiration) { - indicesPerExpTime++ - if indicesPerExpTime > 3 { - return serrors.New("more than three indices per expiration time", - "expiration", indices.GetExpiration(i)) - } - } else { - indicesPerExpTime = 1 - } - if indices.GetIndexNumber(i).Sub(lastIndexNumber) != reservation.IndexNumber(1) { - return serrors.New("non consecutive indices", "prev_index_number", lastIndexNumber, - "index_number", indices.GetIndexNumber(i)) - } - lastExpiration = indices.GetExpiration(i) - lastIndexNumber = indices.GetIndexNumber(i) - token := indices.GetToken(i) - if token != nil { - if token.Idx != lastIndexNumber { - return serrors.New("inconsistent token", "token_index_number", token.Idx, - "expected", lastIndexNumber) - } - if token.ExpirationTick != reservation.TickFromTime(lastExpiration) { - return serrors.New("inconsistent token", "token_expiration_tick", - token.ExpirationTick, "expected", reservation.TickFromTime(lastExpiration)) - } - if token.BWCls != indices.GetAllocBW(i) { - return serrors.New("inconsistent token", "token_bw_class", token.BWCls, - "expected", indices.GetAllocBW(i)) - } - } - } - return nil -} - -// FindIndex returns the slice index for the passed IndexNumber. -func FindIndex(indices IndicesInterface, idx reservation.IndexNumber) (int, error) { - var firstIdx reservation.IndexNumber = 0 - if indices.Len() > 0 { - firstIdx = indices.GetIndexNumber(0) - } - sliceIndex := int(idx.Sub(firstIdx)) - if sliceIndex > indices.Len()-1 { - return 0, serrors.New("index not found in this reservation", "index_number", idx, - "indices length", indices.Len()) - } - return sliceIndex, nil -} - -// SortIndices sorts these Indices according to their index number modulo 16, e.g. [14, 15, 0, 1]. -func SortIndices(idxs IndicesInterface) { - if idxs.Len() < 2 { - return - } - sort.Slice(idxs, func(i, j int) bool { - ae, be := idxs.GetExpiration(i), idxs.GetExpiration(j) - ai, bi := idxs.GetIndexNumber(i), idxs.GetIndexNumber(j) - distance := bi.Sub(ai) - return ae.Before(be) || (ae.Equal(be) && distance < 3) - }) - // find a discontinuity and rotate - i := 1 - for ; i < idxs.Len(); i++ { - if idxs.GetIndexNumber(i-1).Add(1) != idxs.GetIndexNumber(i).Add(0) { - break - } - } - idxs = idxs.Rotate(i) -} diff --git a/control/colibri/reservation/index_test.go b/control/colibri/reservation/index_test.go deleted file mode 100644 index f786564955..0000000000 --- a/control/colibri/reservation/index_test.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservation - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" -) - -func TestValidateIndices(t *testing.T) { - idxs := make(Indices, 0) - // up to 3 indices per expiration time - expTime := time.Unix(1, 0) - idx, err := idxs.NewIndex(expTime) - require.NoError(t, err) - require.Equal(t, reservation.IndexNumber(0), idx) - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.Equal(t, reservation.IndexNumber(1), idx) - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.Equal(t, reservation.IndexNumber(2), idx) - idxs = idxs[:1] - // exp time is less - _, err = idxs.NewIndex(expTime.Add(-1 * time.Millisecond)) - require.Error(t, err) - require.Len(t, idxs, 1) - // exp time is same - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.Len(t, idxs, 2) - require.True(t, idxs[1].Idx == idx) - require.True(t, idxs[1].Expiration == expTime) - require.Equal(t, reservation.IndexNumber(1), idx) - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.Len(t, idxs, 3) - require.True(t, idxs[2].Idx == idx) - require.True(t, idxs[2].Expiration == expTime) - require.Equal(t, reservation.IndexNumber(2), idx) - // too many indices for the same exp time - _, err = idxs.NewIndex(expTime) - require.Error(t, err) - require.Len(t, idxs, 3) - // exp time is greater - expTime = expTime.Add(time.Second) - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.Len(t, idxs, 4) - require.True(t, idxs[3].Idx == idx) - require.True(t, idxs[3].Expiration == expTime) - require.Equal(t, reservation.IndexNumber(3), idx) - // index number rollover - idxs = Indices{} - idxs.NewIndex(expTime) - require.Len(t, idxs, 1) - idxs[0].Idx = idxs[0].Idx.Sub(1) - idx, err = idxs.NewIndex(expTime) - require.NoError(t, err) - require.True(t, idxs[1].Idx == idx) - require.True(t, idxs[1].Expiration == expTime) - require.Equal(t, reservation.IndexNumber(0), idx) - // more than 16 indices - idxs = Indices{} - for i := 0; i < 16; i++ { - expTime := time.Unix(int64(i), 0) - _, err = idxs.NewIndex(expTime) - require.NoError(t, err) - } - require.Len(t, idxs, 16) - _, err = idxs.NewIndex(expTime.Add(time.Hour)) - require.Error(t, err) - // exp time is before - idxs = Indices{} - expTime = time.Unix(1, 0) - idxs.NewIndex(expTime) - idxs.NewIndex(expTime) - idxs[1].Expiration = expTime.Add(-1 * time.Second) - err = ValidateIndices(idxs) - require.Error(t, err) - // non consecutive indices - idxs = Indices{} - expTime = time.Unix(1, 0) - idxs.NewIndex(expTime) - idxs.NewIndex(expTime) - idxs[1].Idx = 2 - err = ValidateIndices(idxs) - require.Error(t, err) - // more than three indices per exp time - idxs = Indices{} - idxs.NewIndex(expTime) - idxs.NewIndex(expTime) - idxs.NewIndex(expTime) - require.Len(t, idxs, 3) - err = ValidateIndices(idxs) - require.NoError(t, err) - _, err = idxs.NewIndex(expTime) - require.Error(t, err) -} - -func TestIndicesSort(t *testing.T) { - indices := newTestIndices(2, 3, 1) - SortIndices(indices) - require.Len(t, indices, 3) - checkIndicesSorted(t, indices) - // one element - indices = newTestIndices(2) - SortIndices(indices) - require.Len(t, indices, 1) - require.Equal(t, 2, int(indices[0].Idx)) - // empty - indices = Indices{} - SortIndices(indices) - require.Len(t, indices, 0) - // wrap around - indices = newTestIndices(0, 1, 15) - SortIndices(indices) - require.Len(t, indices, 3) - checkIndicesSorted(t, indices) - // full 16 elements - indices = newTestIndices(14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) - SortIndices(indices) - require.Len(t, indices, 16) - checkIndicesSorted(t, indices) -} - -func newTestIndices(idxs ...int) Indices { - indices := make(Indices, len(idxs)) - for i, idx := range idxs { - indices[i].Expiration = util.SecsToTime(uint32(i/3 + 1)) - indices[i].Idx = reservation.IndexNumber(idx) - } - return indices -} - -func checkIndicesSorted(t *testing.T, idxs Indices) { - t.Helper() - // validate according to valid indices criteria - err := ValidateIndices(idxs) - require.NoError(t, err) -} - -type Index struct { - Idx reservation.IndexNumber - Expiration time.Time -} - -type Indices []Index - -var _ IndicesInterface = (*Indices)(nil) - -func (idxs Indices) Len() int { return len(idxs) } -func (idxs Indices) GetIndexNumber(i int) reservation.IndexNumber { return idxs[i].Idx } -func (idxs Indices) GetExpiration(i int) time.Time { return idxs[i].Expiration } -func (idxs Indices) GetAllocBW(i int) reservation.BWCls { return reservation.BWCls(0) } -func (idxs Indices) GetToken(i int) *reservation.Token { return nil } -func (idxs Indices) Rotate(i int) IndicesInterface { - return append(idxs[i:], idxs[:i]...) -} - -func (idxs *Indices) NewIndex(expTime time.Time) (reservation.IndexNumber, error) { - idx := reservation.IndexNumber(0) - if len(*idxs) > 0 { - idx = (*idxs)[len(*idxs)-1].Idx.Add(1) - } - index := Index{ - Idx: idx, - Expiration: expTime, - } - newIndices := make(Indices, len(*idxs)+1) - copy(newIndices, *idxs) - newIndices[len(newIndices)-1] = index - err := ValidateIndices(newIndices) - if err != nil { - return reservation.IndexNumber(0), err - } - *idxs = newIndices - return idx, nil -} diff --git a/control/colibri/reservation/request.go b/control/colibri/reservation/request.go deleted file mode 100644 index 6d1c7cef39..0000000000 --- a/control/colibri/reservation/request.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservation - -import ( - "github.com/scionproto/scion/pkg/private/serrors" -) - -// RequestMetadata contains information about the request, such as its forwarding path. -// This base struct can be used by any request or response packets. -type RequestMetadata struct { - path ColibriPath // the path the packet came / will go with -} - -// NewRequestMetadata constructs the base Request type. -func NewRequestMetadata(path ColibriPath) (*RequestMetadata, error) { - if path == nil { - return nil, serrors.New("new request with nil path") - } - return &RequestMetadata{ - path: path.Copy(), - }, nil -} - -// Path returns the ColibriPath in this metadata. -func (m *RequestMetadata) Path() ColibriPath { - return m.path -} - -// IsLastAS returns true if this hop is the last one (this AS is the destination). -func (m *RequestMetadata) IsLastAS() bool { - return m.path.IndexOfCurrentHop() == m.path.NumberOfHops()-1 -} diff --git a/control/colibri/reservation/reservationdbtest/BUILD.bazel b/control/colibri/reservation/reservationdbtest/BUILD.bazel deleted file mode 100644 index 76917fb9d7..0000000000 --- a/control/colibri/reservation/reservationdbtest/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["reservationdbtest.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/reservationdbtest", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservation/segmenttest:go_default_library", - "//control/colibri/reservationstorage/backend:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/util:go_default_library", - "//pkg/private/xtest:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/reservationdbtest/reservationdbtest.go b/control/colibri/reservation/reservationdbtest/reservationdbtest.go deleted file mode 100644 index 89ccac7ddb..0000000000 --- a/control/colibri/reservation/reservationdbtest/reservationdbtest.go +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservationdbtest - -import ( - "context" - "encoding/binary" - "encoding/hex" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/e2e" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segmenttest" - "github.com/scionproto/scion/control/colibri/reservationstorage/backend" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" - "github.com/scionproto/scion/pkg/private/xtest" -) - -type TestableDB interface { - backend.DB - Prepare(*testing.T, context.Context) -} - -func TestDB(t *testing.T, db TestableDB) { - tests := map[string]func(context.Context, *testing.T, backend.DB){ - "insert segment reservations create ID": testNewSegmentRsv, - "persist segment reservation": testPersistSegmentRsv, - "get segment reservation from ID": testGetSegmentRsvFromID, - "get segment reservations from src/dst": testGetSegmentRsvsFromSrcDstIA, - "get segment reservation from path": testGetSegmentRsvFromPath, - "get all segment reservations": testGetAllSegmentRsvs, - "get segment reservation from IF pair": testGetSegmentRsvsFromIFPair, - "delete segment reservation": testDeleteSegmentRsv, - "delete expired indices": testDeleteExpiredIndices, - "persist e2e reservation": testPersistE2ERsv, - "get e2e reservation from ID": testGetE2ERsvFromID, - "get e2e reservations from segment ones": testGetE2ERsvsOnSegRsv, - } - for name, test := range tests { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - db.Prepare(t, ctx) - test(ctx, t, db) - }) - } -} - -func testNewSegmentRsv(ctx context.Context, t *testing.T, db backend.DB) { - r := newTestReservation(t) - r.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - r.Indices = segment.Indices{} - // no indices - err := db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - require.Equal(t, xtest.MustParseHexString("00000001"), r.ID.Suffix[:]) - rsv, err := db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // at least one index, and change path - token := newToken() - _, err = r.NewIndexFromToken(token, 0, 0) - require.NoError(t, err) - r.Path = segmenttest.NewPathFromComponents(1, "1-ff00:0:1", 2, 1, "1-ff00:0:2", 0) - err = db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - require.Equal(t, xtest.MustParseHexString("00000002"), r.ID.Suffix[:]) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // same path should fail - err = db.NewSegmentRsv(ctx, r) - require.Error(t, err) - // different ASID should start with the lowest suffix - r = newTestReservation(t) - r.ID.ASID = xtest.MustParseAS("ff00:1234:1") - err = db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - require.Equal(t, xtest.MustParseHexString("00000001"), r.ID.Suffix[:]) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) -} - -func testPersistSegmentRsv(ctx context.Context, t *testing.T, db backend.DB) { - r := newTestReservation(t) - for i := uint32(1); i < 10; i++ { - _, err := r.NewIndexAtSource(util.SecsToTime(i), 0, 0, 0, 0, reservation.CorePath) - require.NoError(t, err) - } - require.Len(t, r.Indices, 10) - err := db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - rsv, err := db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // now remove one index - err = r.RemoveIndex(0) - require.NoError(t, err) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // change ID - r.ID.ASID = xtest.MustParseAS("ff00:1:12") - copy(r.ID.Suffix[:], xtest.MustParseHexString("beefcafe")) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // change attributes - r.Ingress = 3 - r.Egress = 4 - r.Path = segmenttest.NewPathFromComponents(3, "1-ff00:0:1", 11, 1, "1-ff00:0:2", 0) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // remove 7 more indices, remains 1 index - err = r.RemoveIndex(8) - require.NoError(t, err) - r.Indices[0].Expiration = util.SecsToTime(12345) - r.Indices[0].MinBW = 10 - r.Indices[0].MaxBW = 11 - r.Indices[0].AllocBW = 12 - r.Indices[0].Token = newToken() // change the token - r.Indices[0].Token.BWCls = 8 - err = r.SetIndexConfirmed(r.Indices[0].Idx) - require.NoError(t, err) - err = r.SetIndexActive(r.Indices[0].Idx) - require.NoError(t, err) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) -} - -func testGetSegmentRsvFromID(ctx context.Context, t *testing.T, db backend.DB) { - r := newTestReservation(t) - err := db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - // create new index - expTime := util.SecsToTime(1) - _, err = r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.NoError(t, err) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - r2, err := db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, r2) - // 14 more indices for a total of 16 - require.Len(t, r.Indices, 2) - for i := 2; i < 16; i++ { - expTime = util.SecsToTime(uint32(i)) - _, err = r.NewIndexAtSource(expTime, reservation.BWCls(i), reservation.BWCls(i), - reservation.BWCls(i), 0, reservation.CorePath) - require.NoError(t, err) - } - require.Len(t, r.Indices, 16) - err = db.PersistSegmentRsv(ctx, r) - require.NoError(t, err) - r2, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, r2) - // wrong ID - ID := r.ID - ID.ASID++ - r2, err = db.GetSegmentRsvFromID(ctx, &ID) - require.NoError(t, err) - require.Nil(t, r2) -} - -func testGetSegmentRsvsFromSrcDstIA(ctx context.Context, t *testing.T, db backend.DB) { - r := newTestReservation(t) - r.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - err := db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - rsvs, err := db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), r.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 1) - require.Equal(t, r, rsvs[0]) - // another reservation with same source and destination - r2 := newTestReservation(t) - r2.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0) - err = db.NewSegmentRsv(ctx, r2) - require.NoError(t, err) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), r.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 2) - // compare without order - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2}) - // one more with same source different destination - r3 := newTestReservation(t) - r3.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:3", 0) - err = db.NewSegmentRsv(ctx, r3) - require.NoError(t, err) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), r.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 2) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2}) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), 0) - require.NoError(t, err) - require.Len(t, rsvs, 3) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2, r3}) - // another reservation with unique source but same destination as r3 - r4 := newTestReservation(t) - r4.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:4", 1, 1, "1-ff00:0:3", 0) - err = db.NewSegmentRsv(ctx, r4) - require.NoError(t, err) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), r.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 2) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2}) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, r.Path.GetSrcIA(), 0) - require.NoError(t, err) - require.Len(t, rsvs, 3) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2, r3}) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, 0, r.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 2) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r, r2}) - rsvs, err = db.GetSegmentRsvsFromSrcDstIA(ctx, 0, r3.Path.GetDstIA()) - require.NoError(t, err) - require.Len(t, rsvs, 2) - require.ElementsMatch(t, rsvs, []*segment.Reservation{r3, r4}) -} - -func testGetSegmentRsvFromPath(ctx context.Context, t *testing.T, db backend.DB) { - r1 := newTestReservation(t) - r1.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - err := db.NewSegmentRsv(ctx, r1) - require.NoError(t, err) - r2 := newTestReservation(t) - r2.Path = segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:3", 0) - err = db.NewSegmentRsv(ctx, r2) - require.NoError(t, err) - // retrieve - r, err := db.GetSegmentRsvFromPath(ctx, r1.Path) - require.NoError(t, err) - require.Equal(t, r1, r) - r, err = db.GetSegmentRsvFromPath(ctx, r2.Path) - require.NoError(t, err) - require.Equal(t, r2, r) -} - -func testGetAllSegmentRsvs(ctx context.Context, t *testing.T, db backend.DB) { - // empty - rsvs, err := db.GetAllSegmentRsvs(ctx) - require.NoError(t, err) - require.Empty(t, rsvs) - // insert in1,eg1 ; in2,eg1 ; in1,eg2 - r1 := newTestReservation(t) - r1.Ingress = 11 - r1.Egress = 12 - err = db.NewSegmentRsv(ctx, r1) - require.NoError(t, err) - r2 := newTestReservation(t) - r2.Ingress = 21 - r2.Egress = 12 - err = db.NewSegmentRsv(ctx, r2) - require.NoError(t, err) - r3 := newTestReservation(t) - r3.Ingress = 11 - r3.Egress = 22 - err = db.NewSegmentRsv(ctx, r3) - require.NoError(t, err) - // retrieve them - rsvs, err = db.GetAllSegmentRsvs(ctx) - require.NoError(t, err) - expected := []*segment.Reservation{r1, r2, r3} - require.ElementsMatch(t, expected, rsvs) -} - -func testGetSegmentRsvsFromIFPair(ctx context.Context, t *testing.T, db backend.DB) { - // insert in1,e1 ; in2,e1 ; in1,e2 - r1 := newTestReservation(t) - r1.Ingress = 11 - r1.Egress = 12 - err := db.NewSegmentRsv(ctx, r1) - require.NoError(t, err) - r2 := newTestReservation(t) - r2.Ingress = 21 - r2.Egress = 12 - err = db.NewSegmentRsv(ctx, r2) - require.NoError(t, err) - r3 := newTestReservation(t) - r3.Ingress = 11 - r3.Egress = 22 - err = db.NewSegmentRsv(ctx, r3) - require.NoError(t, err) - // query with a specific pair - rsvs, err := db.GetSegmentRsvsFromIFPair(ctx, &r1.Ingress, &r1.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 1) - expected := []*segment.Reservation{r1} - require.ElementsMatch(t, expected, rsvs) - // any ingress - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, nil, &r1.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 2) - expected = []*segment.Reservation{r1, r2} - require.ElementsMatch(t, expected, rsvs) - // any egress - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r1.Ingress, nil) - require.NoError(t, err) - require.Len(t, rsvs, 2) - expected = []*segment.Reservation{r1, r3} - require.ElementsMatch(t, expected, rsvs) - // no matches - var inexistentIngress uint16 = 222 - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &inexistentIngress, nil) - require.NoError(t, err) - require.Len(t, rsvs, 0) - // bad query - _, err = db.GetSegmentRsvsFromIFPair(ctx, nil, nil) - require.Error(t, err) -} - -func testDeleteSegmentRsv(ctx context.Context, t *testing.T, db backend.DB) { - r := newTestReservation(t) - err := db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - err = db.DeleteSegmentRsv(ctx, &r.ID) - require.NoError(t, err) - rsv, err := db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Nil(t, rsv) - // with no indices - r = newTestReservation(t) - r.Indices = segment.Indices{} - err = db.NewSegmentRsv(ctx, r) - require.NoError(t, err) - err = db.DeleteSegmentRsv(ctx, &r.ID) - require.NoError(t, err) - rsv, err = db.GetSegmentRsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Nil(t, rsv) -} - -func testDeleteExpiredIndices(ctx context.Context, t *testing.T, db backend.DB) { - // create seg. and e2e reservations that have indices expiring at different times. - // rX stands for segment rsv. and eX for an e2e. Twice the same symbol means another index. - // timeline: e1...r1...r2,r3...e2,e3...e3,e4...r3,r4...e5 - // 1 2 3 4 5 6 1000 - // Each eX is linked to a rX, being X the same for both. But e5 is linked to r4. - - // r1, e1 - segIds := make([]reservation.SegmentID, 0) - r := newTestReservation(t) - r.Indices[0].Expiration = util.SecsToTime(2) - err := db.NewSegmentRsv(ctx, r) // save r1 - require.NoError(t, err) - segIds = append(segIds, r.ID) - e := newTestE2EReservation(t) - e.ID.ASID = xtest.MustParseAS("ff00:0:1") - e.SegmentReservations = []*segment.Reservation{r} - e.Indices[0].Expiration = util.SecsToTime(1) - err = db.PersistE2ERsv(ctx, e) // save e1 - require.NoError(t, err) - // r2, e2 - r.Indices[0].Expiration = util.SecsToTime(3) - err = db.NewSegmentRsv(ctx, r) // save r2 - require.NoError(t, err) - segIds = append(segIds, r.ID) - e.ID.ASID = xtest.MustParseAS("ff00:0:2") - e.SegmentReservations = []*segment.Reservation{r} - e.Indices[0].Expiration = util.SecsToTime(4) - err = db.PersistE2ERsv(ctx, e) // save e2 - require.NoError(t, err) - // r3, e3 - r.Indices[0].Expiration = util.SecsToTime(3) - r.NewIndexAtSource(util.SecsToTime(6), 1, 3, 2, 5, reservation.CorePath) - err = db.NewSegmentRsv(ctx, r) // save r3 - require.NoError(t, err) - segIds = append(segIds, r.ID) - e.ID.ASID = xtest.MustParseAS("ff00:0:3") - e.SegmentReservations = []*segment.Reservation{r} - e.Indices[0].Expiration = util.SecsToTime(4) - _, err = e.NewIndex(util.SecsToTime(5)) - require.NoError(t, err) - err = db.PersistE2ERsv(ctx, e) // save e3 - require.NoError(t, err) - // r4, e4 - r.Indices = r.Indices[:1] - r.Indices[0].Expiration = util.SecsToTime(6) - err = db.NewSegmentRsv(ctx, r) // save r4 - require.NoError(t, err) - segIds = append(segIds, r.ID) - e.Indices = e.Indices[:1] - e.Indices[0].Expiration = util.SecsToTime(5) - e.ID.ASID = xtest.MustParseAS("ff00:0:4") - e.SegmentReservations = []*segment.Reservation{r} - err = db.PersistE2ERsv(ctx, e) // save e4 - require.NoError(t, err) - // e5 - e.ID.ASID = xtest.MustParseAS("ff00:0:5") - e.SegmentReservations = []*segment.Reservation{r} - e.Indices[0].Expiration = util.SecsToTime(1000) - err = db.PersistE2ERsv(ctx, e) // save e5 - require.NoError(t, err) - - // second 1: nothing deleted - c, err := db.DeleteExpiredIndices(ctx, util.SecsToTime(1)) - require.NoError(t, err) - require.Equal(t, 0, c) - rsvs, err := db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) // get all seg rsvs - require.NoError(t, err) - require.Len(t, rsvs, 4) - e2es := getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 5) - // second 2, in DB: r1...r2,r3...e2,e3...e3,e4...r3,r4...e5 - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(2)) - require.NoError(t, err) - require.Equal(t, 1, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 4) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 4) - // second 3: in DB: r2,r3...e2,e3...e3,e4...r3,r4...e5 - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(3)) - require.NoError(t, err) - require.Equal(t, 1, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 3) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 4) - // second 4: in DB: e2,e3...e3,e4...r3,r4...e5 - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(4)) - require.NoError(t, err) - require.Equal(t, 2, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 2) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 3) // r2 is gone, cascades for e2 - // second 5: in DB: e3,e4...r3,r4...e5 - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(5)) - require.NoError(t, err) - require.Equal(t, 2, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 2) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 3) - // second 6: in DB: r3,r4...e5 - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(6)) - require.NoError(t, err) - require.Equal(t, 2, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 2) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 1) - // second 7, in DB: nothing - c, err = db.DeleteExpiredIndices(ctx, util.SecsToTime(7)) - require.NoError(t, err) - require.Equal(t, 2, c) - rsvs, err = db.GetSegmentRsvsFromIFPair(ctx, &r.Ingress, &r.Egress) - require.NoError(t, err) - require.Len(t, rsvs, 0) - e2es = getAllE2ERsvsOnSegmentRsvs(ctx, t, db, segIds) - require.Len(t, e2es, 0) // r4 is gone, cascades for e5 -} - -func testPersistE2ERsv(ctx context.Context, t *testing.T, db backend.DB) { - r1 := newTestE2EReservation(t) - for _, seg := range r1.SegmentReservations { - err := db.PersistSegmentRsv(ctx, seg) - require.NoError(t, err) - } - err := db.PersistE2ERsv(ctx, r1) - require.NoError(t, err) - // get it back - rsv, err := db.GetE2ERsvFromID(ctx, &r1.ID) - require.NoError(t, err) - require.Equal(t, r1, rsv) - // modify - r2 := rsv - for i := range r2.ID.Suffix { - r2.ID.Suffix[i] = byte(i) - } - for i := uint32(2); i < 16; i++ { // add 14 more indices - _, err = r2.NewIndex(util.SecsToTime(i)) - require.NoError(t, err) - } - for i := 0; i < 2; i++ { - seg := newTestReservation(t) - seg.ID.ASID = xtest.MustParseAS(fmt.Sprintf("ff00:2:%d", i+1)) - for j := uint32(1); j < 16; j++ { - _, err := seg.NewIndexAtSource(util.SecsToTime(j), 1, 3, 2, 5, reservation.CorePath) - require.NoError(t, err) - } - err := db.PersistSegmentRsv(ctx, seg) - require.NoError(t, err) - r2.SegmentReservations = append(r2.SegmentReservations, seg) - } - err = db.PersistE2ERsv(ctx, r2) - require.NoError(t, err) - rsv, err = db.GetE2ERsvFromID(ctx, &r2.ID) - require.NoError(t, err) - require.Equal(t, r2, rsv) - // check the other reservation was left intact - rsv, err = db.GetE2ERsvFromID(ctx, &r1.ID) - require.NoError(t, err) - require.Equal(t, r1, rsv) - // try to persist an e2e reservation without persisting its associated segment reservation - r := newTestE2EReservation(t) - r.SegmentReservations[0].ID.ASID = xtest.MustParseAS("ff00:3:1") - err = db.PersistE2ERsv(ctx, r) - require.Error(t, err) - // after persisting the segment one, it will work - err = db.PersistSegmentRsv(ctx, r.SegmentReservations[0]) - require.NoError(t, err) - err = db.PersistE2ERsv(ctx, r) - require.NoError(t, err) -} - -func testGetE2ERsvFromID(ctx context.Context, t *testing.T, db backend.DB) { - // create several e2e reservations, with one segment reservations in common, and two not - checkThisRsvs := map[int]*e2e.Reservation{1: nil, 16: nil, 50: nil, 100: nil} - for i := 1; i <= 100; i++ { - r := newTestE2EReservation(t) - binary.BigEndian.PutUint32(r.ID.Suffix[:], uint32(i)) - _, found := checkThisRsvs[i] - if found { - checkThisRsvs[i] = r - } - for j := 0; j < 2; j++ { - seg := newTestReservation(t) - seg.ID.ASID = xtest.MustParseAS(fmt.Sprintf("ff00:%d:%d", i, j+1)) - err := db.PersistSegmentRsv(ctx, seg) - require.NoError(t, err) - } - for _, seg := range r.SegmentReservations { - segRsv, err := db.GetSegmentRsvFromID(ctx, &seg.ID) - require.NoError(t, err) - if segRsv == nil { - err := db.PersistSegmentRsv(ctx, seg) - require.NoError(t, err) - } - } - err := db.PersistE2ERsv(ctx, r) - require.NoError(t, err) - } - // now check - for i, r := range checkThisRsvs { - ID := reservation.E2EID{ASID: xtest.MustParseAS("ff00:0:1")} - binary.BigEndian.PutUint32(ID.Suffix[:], uint32(i)) - rsv, err := db.GetE2ERsvFromID(ctx, &ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - } - // with 8 indices starting at index number 14 - r := newTestE2EReservation(t) - r.Indices = e2e.Indices{} - for i := uint32(2); i < 18; i++ { - _, err := r.NewIndex(util.SecsToTime(i / 2)) - require.NoError(t, err) - } - r.Indices = r.Indices[14:] - for i := uint32(18); i < 20; i++ { - _, err := r.NewIndex(util.SecsToTime(i / 2)) - require.NoError(t, err) - } - err := db.PersistE2ERsv(ctx, r) - require.NoError(t, err) - rsv, err := db.GetE2ERsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // 16 indices - require.Len(t, r.Indices, 4) - for i := uint32(20); i < 32; i++ { - _, err := r.NewIndex(util.SecsToTime(i / 2)) - require.NoError(t, err) - } - require.Len(t, r.Indices, 16) - err = db.PersistE2ERsv(ctx, r) - require.NoError(t, err) - rsv, err = db.GetE2ERsvFromID(ctx, &r.ID) - require.NoError(t, err) - require.Equal(t, r, rsv) - // not present in DB - ID, err := reservation.NewE2EID(xtest.MustParseAS("ff00:2222:3333"), - xtest.MustParseHexString("0123456789abcdef0123")) - require.NoError(t, err) - rsv, err = db.GetE2ERsvFromID(ctx, ID) - require.NoError(t, err) - require.Nil(t, rsv) -} - -func testGetE2ERsvsOnSegRsv(ctx context.Context, t *testing.T, db backend.DB) { - s1 := newTestReservation(t) - err := db.NewSegmentRsv(ctx, s1) - require.NoError(t, err) - s2 := newTestReservation(t) - err = db.NewSegmentRsv(ctx, s2) - require.NoError(t, err) - // e2e reservations - e1 := newTestE2EReservation(t) - e1.ID.ASID = xtest.MustParseAS("ff00:0:1") - e1.SegmentReservations = []*segment.Reservation{s1} - err = db.PersistE2ERsv(ctx, e1) - require.NoError(t, err) - e2 := newTestE2EReservation(t) - e2.ID.ASID = xtest.MustParseAS("ff00:0:2") - e2.SegmentReservations = []*segment.Reservation{s2} - err = db.PersistE2ERsv(ctx, e2) - require.NoError(t, err) - e3 := newTestE2EReservation(t) - e3.ID.ASID = xtest.MustParseAS("ff00:0:3") - e3.SegmentReservations = []*segment.Reservation{s1, s2} - err = db.PersistE2ERsv(ctx, e3) - require.NoError(t, err) - // test - rsvs, err := db.GetE2ERsvsOnSegRsv(ctx, &s1.ID) - require.NoError(t, err) - require.ElementsMatch(t, rsvs, []*e2e.Reservation{e1, e3}) - rsvs, err = db.GetE2ERsvsOnSegRsv(ctx, &s2.ID) - require.NoError(t, err) - require.ElementsMatch(t, rsvs, []*e2e.Reservation{e2, e3}) -} - -// newToken just returns a token that can be serialized. This one has two HopFields. -func newToken() *reservation.Token { - t, err := reservation.TokenFromRaw(xtest.MustParseHexString( - "0000000000040500003f001002bad1ce003f001002facade")) - if err != nil { - panic("invalid serialized token") - } - return t -} - -func newTestReservation(t *testing.T) *segment.Reservation { - t.Helper() - r := segment.NewReservation() - r.Path = segment.ReservationTransparentPath{} - r.ID.ASID = xtest.MustParseAS("ff00:0:1") - r.Ingress = 0 - r.Egress = 1 - r.TrafficSplit = 3 - r.PathEndProps = reservation.EndLocal | reservation.StartLocal - expTime := util.SecsToTime(1) - _, err := r.NewIndexAtSource(expTime, 1, 3, 2, 5, reservation.CorePath) - require.NoError(t, err) - err = r.SetIndexConfirmed(0) - require.NoError(t, err) - return r -} - -func newTestE2EReservation(t *testing.T) *e2e.Reservation { - rsv := &e2e.Reservation{ - ID: reservation.E2EID{ - ASID: xtest.MustParseAS("ff00:0:1"), - }, - SegmentReservations: []*segment.Reservation{ - newTestReservation(t), - }, - } - expTime := util.SecsToTime(1) - _, err := rsv.NewIndex(expTime) - require.NoError(t, err) - return rsv -} - -func getAllE2ERsvsOnSegmentRsvs(ctx context.Context, t *testing.T, db backend.DB, - ids []reservation.SegmentID) []*e2e.Reservation { - - set := make(map[string]struct{}) - rsvs := make([]*e2e.Reservation, 0) - for _, id := range ids { - rs, err := db.GetE2ERsvsOnSegRsv(ctx, &id) - require.NoError(t, err) - for _, r := range rs { - s := hex.EncodeToString(r.ID.ToRaw()) - _, found := set[s] - if !found { - rsvs = append(rsvs, r) - set[s] = struct{}{} - } - } - } - return rsvs -} diff --git a/control/colibri/reservation/segment/BUILD.bazel b/control/colibri/reservation/segment/BUILD.bazel deleted file mode 100644 index 99ca4a643c..0000000000 --- a/control/colibri/reservation/segment/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "index.go", - "path.go", - "request.go", - "reservation.go", - "response.go", - ], - importpath = "github.com/scionproto/scion/control/colibri/reservation/segment", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = [ - "export_test.go", - "path_test.go", - "reservation_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//control/colibri/reservation/segmenttest:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/util:go_default_library", - "//pkg/private/xtest:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/segment/admission/BUILD.bazel b/control/colibri/reservation/segment/admission/BUILD.bazel deleted file mode 100644 index bb24646544..0000000000 --- a/control/colibri/reservation/segment/admission/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["admitter.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/segment/admission", - visibility = ["//visibility:public"], - deps = ["//control/colibri/reservation/segment:go_default_library"], -) diff --git a/control/colibri/reservation/segment/admission/admitter.go b/control/colibri/reservation/segment/admission/admitter.go deleted file mode 100644 index aef129ecb0..0000000000 --- a/control/colibri/reservation/segment/admission/admitter.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 admission - -import ( - "context" - - "github.com/scionproto/scion/control/colibri/reservation/segment" -) - -// Admitter specifies what an admission entity has to implement to govern the segment admission. -type Admitter interface { - // req will be modified with the allowed and maximum bandwidths if they were computed. - // It can also return an error. - AdmitRsv(ctx context.Context, req *segment.SetupReq) error -} diff --git a/control/colibri/reservation/segment/admission/impl/BUILD.bazel b/control/colibri/reservation/segment/admission/impl/BUILD.bazel deleted file mode 100644 index 69600c1f87..0000000000 --- a/control/colibri/reservation/segment/admission/impl/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["stateless.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/segment/admission/impl", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservation/segment/admission:go_default_library", - "//control/colibri/reservationstorage/backend:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["stateless_test.go"], - embed = [":go_default_library"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservationstorage/backend/mock_backend:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/util:go_default_library", - "//pkg/private/xtest:go_default_library", - "@com_github_golang_mock//gomock:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/segment/admission/impl/stateless.go b/control/colibri/reservation/segment/admission/impl/stateless.go deleted file mode 100644 index 13c5bdeeaf..0000000000 --- a/control/colibri/reservation/segment/admission/impl/stateless.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 impl - -import ( - "context" - "math" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segment/admission" - "github.com/scionproto/scion/control/colibri/reservationstorage/backend" - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// StatelessAdmission can admit a segment reservation without any state other than the DB. -type StatelessAdmission struct { - DB backend.DB - Capacities base.Capacities // aka capacity matrix - Delta float64 // fraction of free BW that can be reserved in one request -} - -var _ admission.Admitter = (*StatelessAdmission)(nil) - -// AdmitRsv admits a segment reservation. The request will be modified with the allowed and -// maximum bandwidths if they were computed. It can also return an error that must be checked. -func (a *StatelessAdmission) AdmitRsv(ctx context.Context, req *segment.SetupReq) error { - avail, err := a.availableBW(ctx, req) - if err != nil { - return serrors.WrapStr("cannot compute available bandwidth", err, "segment_id", req.ID) - } - ideal, err := a.idealBW(ctx, req) - if err != nil { - return serrors.WrapStr("cannot compute ideal bandwidth", err, "segment_id", req.ID) - } - maxAlloc := reservation.BWClsFromBW(minBW(avail, ideal)) - bead := reservation.AllocationBead{ - AllocBW: reservation.MinBWCls(maxAlloc, req.MaxBW), - MaxBW: maxAlloc, - } - req.AllocTrail = append(req.AllocTrail, bead) - if maxAlloc < req.MinBW { - return serrors.New("admission denied", "maxalloc", maxAlloc, "minbw", req.MinBW, - "segment_id", req.ID) - } - return nil -} - -func (a *StatelessAdmission) availableBW(ctx context.Context, req *segment.SetupReq) ( - uint64, error) { - - sameIngress, err := a.DB.GetSegmentRsvsFromIFPair(ctx, &req.Ingress, nil) - if err != nil { - return 0, serrors.WrapStr("cannot get reservations using ingress", err, - "ingress", req.Ingress) - } - sameEgress, err := a.DB.GetSegmentRsvsFromIFPair(ctx, nil, &req.Egress) - if err != nil { - return 0, serrors.WrapStr("cannot get reservations using egress", err, - "egress", req.Egress) - } - bwIngress := sumMaxBlockedBW(sameIngress, req.ID) - freeIngress := a.Capacities.CapacityIngress(req.Ingress) - bwIngress - bwEgress := sumMaxBlockedBW(sameEgress, req.ID) - freeEgress := a.Capacities.CapacityEgress(req.Egress) - bwEgress - // `free` excludes the BW from an existing reservation if its ID equals the request's ID - free := float64(minBW(freeIngress, freeEgress)) - return uint64(free * a.Delta), nil -} - -func (a *StatelessAdmission) idealBW(ctx context.Context, req *segment.SetupReq) (uint64, error) { - demsPerSrcRegIngress, err := a.computeTempDemands(ctx, req.Ingress, req) - if err != nil { - return 0, serrors.WrapStr("cannot compute temporary demands", err) - } - tubeRatio, err := a.tubeRatio(ctx, req, demsPerSrcRegIngress) - if err != nil { - return 0, serrors.WrapStr("cannot compute tube ratio", err) - } - linkRatio, err := a.linkRatio(ctx, req, demsPerSrcRegIngress) - if err != nil { - return 0, serrors.WrapStr("cannot compute link ratio", err) - } - cap := float64(a.Capacities.CapacityEgress(req.Egress)) - return uint64(cap * tubeRatio * linkRatio), nil -} - -func (a *StatelessAdmission) tubeRatio(ctx context.Context, req *segment.SetupReq, - demsPerSrc demPerSource) (float64, error) { - - // TODO(juagargi) to avoid calling several times to computeTempDemands, refactor the - // type holding the results, so that it stores capReqDem per source per ingress interface. - // InScalFctr and EgScalFctr will be stored independently, per source per interface. - transitDemand, err := a.transitDemand(ctx, req, req.Ingress, demsPerSrc) - if err != nil { - return 0, serrors.WrapStr("cannot compute transit demand", err) - } - capIn := a.Capacities.CapacityIngress(req.Ingress) - numerator := minBW(capIn, transitDemand) - var sum uint64 - for _, in := range a.Capacities.IngressInterfaces() { - demandsForThisIngress, err := a.computeTempDemands(ctx, in, req) - if err != nil { - return 0, serrors.WrapStr("cannot compute transit demand", err) - } - dem, err := a.transitDemand(ctx, req, in, demandsForThisIngress) - if err != nil { - return 0, serrors.WrapStr("cannot compute transit demand", err) - } - sum += minBW(a.Capacities.CapacityIngress(in), dem) - } - return float64(numerator) / float64(sum), nil -} - -func (a *StatelessAdmission) linkRatio(ctx context.Context, req *segment.SetupReq, - demsPerSrc demPerSource) (float64, error) { - - capEg := a.Capacities.CapacityEgress(req.Egress) - demEg := demsPerSrc[req.ID.ASID].eg - - prevBW := req.AllocTrail.MinMax().ToKbps() // min of maxBW in the trail - var egScalFctr float64 - if demEg != 0 { - egScalFctr = float64(minBW(capEg, demEg)) / float64(demEg) - } - numerator := egScalFctr * float64(prevBW) - egScalFctrs := make(map[addr.AS]float64) - for src, dem := range demsPerSrc { - var egScalFctr float64 - if dem.eg != 0 { - egScalFctr = float64(minBW(capEg, dem.eg)) / float64(dem.eg) - } - egScalFctrs[src] = egScalFctr - } - rsvs, err := a.DB.GetAllSegmentRsvs(ctx) - if err != nil { - return 0, serrors.WrapStr("cannot list all reservations", err) - } - srcAllocPerSrc := make(map[addr.AS]uint64) - for _, rsv := range rsvs { - if rsv.ID == req.ID { - continue - } - src := rsv.ID.ASID - srcAlloc := rsv.MaxBlockedBW() - srcAllocPerSrc[src] += srcAlloc - } - if _, found := srcAllocPerSrc[req.ID.ASID]; !found { - // add the source of the request, if not already present - srcAllocPerSrc[req.ID.ASID] = 0 // the value of the srcAlloc itself won't be used - } - // TODO(juagargi) after debugging, integrate this loop into the previous one: - var denom float64 - for src, srcAlloc := range srcAllocPerSrc { - if src == req.ID.ASID { - srcAlloc += prevBW - } - egScalFctr, found := egScalFctrs[src] - if !found { - return 0, serrors.New("cannot compute link ratio, internal error: "+ - "source not found in the egress scale factors", "src", src) - } - denom += float64(srcAlloc) * egScalFctr - } - return numerator / denom, nil -} - -// demands represents the demands for a given source, and a specific ingress-egress interface pair. -// from the admission spec: srcDem, inDem and egDem for a given source. -type demands struct { - src, in, eg uint64 -} - -// demsPerSrc is used in the transit demand computation. -type demPerSource map[addr.AS]demands - -// computeTempDemands will compute inDem, egDem and srcDem grouped by source, for all sources. -// this is, all cap. requested demands from all reservations, grouped by source, that enter -// the AS at "ingress" and exit at "egress". It also stores all the source demands that enter -// the AS at "ingress", and the source demands that exit the AS at "egress". -func (a *StatelessAdmission) computeTempDemands(ctx context.Context, ingress uint16, - req *segment.SetupReq) (demPerSource, error) { - - // TODO(juagargi) consider adding a call to db to get all srcDem,inDem,egDem grouped by source - rsvs, err := a.DB.GetAllSegmentRsvs(ctx) - if err != nil { - return nil, serrors.WrapStr("cannot obtain segment rsvs. from ingress/egress pair", err) - } - capIn := a.Capacities.CapacityIngress(ingress) - capEg := a.Capacities.CapacityEgress(req.Egress) - // srcDem, inDem and egDem grouped by source - demsPerSrc := make(demPerSource) - for _, rsv := range rsvs { - if rsv.ID == req.ID { - continue - } - dem := min3BW(capIn, capEg, rsv.MaxRequestedBW()) // capReqDem in the formulas - bucket := demsPerSrc[rsv.ID.ASID] - if rsv.Ingress == ingress { - bucket.in += dem - } - if rsv.Egress == req.Egress { - bucket.eg += dem - } - if rsv.Ingress == ingress && rsv.Egress == req.Egress { - bucket.src += dem - } - demsPerSrc[rsv.ID.ASID] = bucket - } - // add the request itself to whatever we have for that source - bucket := demsPerSrc[req.ID.ASID] - dem := min3BW(capIn, capEg, req.MaxBW.ToKbps()) - if req.Ingress == ingress { - bucket.in += dem - } - if req.Egress == req.Egress { - bucket.eg += dem - } - if req.Ingress == ingress && req.Egress == req.Egress { - bucket.src += dem - } - demsPerSrc[req.ID.ASID] = bucket - - return demsPerSrc, nil -} - -// transitDemand computes the transit demand from ingress to req.Egress. The parameter -// demsPerSrc must hold the inDem, egDem and srcDem of all reservations, grouped by source, and -// for an ingress interface = ingress parameter. -func (a *StatelessAdmission) transitDemand(ctx context.Context, req *segment.SetupReq, - ingress uint16, demsPerSrc demPerSource) (uint64, error) { - - capIn := a.Capacities.CapacityIngress(ingress) - capEg := a.Capacities.CapacityEgress(req.Egress) - // TODO(juagargi) adjSrcDem is not needed, remove after finishing debugging the admission - adjSrcDem := make(map[addr.AS]uint64) // every adjSrcDem grouped by source - for src, dems := range demsPerSrc { - var inScalFctr float64 = 1. - if dems.in != 0 { - inScalFctr = float64(minBW(capIn, dems.in)) / float64(dems.in) - } - var egScalFctr float64 = 1. - if dems.eg != 0 { - egScalFctr = float64(minBW(capEg, dems.eg)) / float64(dems.eg) - } - adjSrcDem[src] = uint64(math.Min(inScalFctr, egScalFctr) * float64(dems.src)) - } - // now reduce adjSrcDem - var transitDem uint64 - for _, dem := range adjSrcDem { - transitDem += dem - } - - return transitDem, nil -} - -// sumMaxBlockedBW adds up all the max blocked bandwidth by the reservation, for all reservations, -// iff they don't have the same ID as "excludeThisRsv". -func sumMaxBlockedBW(rsvs []*segment.Reservation, excludeThisRsv reservation.SegmentID) uint64 { - var total uint64 - for _, r := range rsvs { - if r.ID != excludeThisRsv { - total += r.MaxBlockedBW() - } - } - return total -} - -func minBW(a, b uint64) uint64 { - if a < b { - return a - } - return b -} - -func min3BW(a, b, c uint64) uint64 { - return minBW(minBW(a, b), c) -} diff --git a/control/colibri/reservation/segment/admission/impl/stateless_test.go b/control/colibri/reservation/segment/admission/impl/stateless_test.go deleted file mode 100644 index 7a024efeba..0000000000 --- a/control/colibri/reservation/segment/admission/impl/stateless_test.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 impl - -import ( - "context" - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservationstorage/backend/mock_backend" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestSumMaxBlockedBW(t *testing.T) { - cases := map[string]struct { - blockedBW uint64 - rsvsFcn func() []*segment.Reservation - excludeID string - }{ - "empty": { - blockedBW: 0, - rsvsFcn: func() []*segment.Reservation { - return nil - }, - excludeID: "ff0000010001beefcafe", - }, - "one reservation": { - blockedBW: reservation.BWCls(5).ToKbps(), - rsvsFcn: func() []*segment.Reservation { - rsv := testNewRsv(t, "ff00:1:1", "01234567", 1, 2, 5, 5, 5) - _, err := rsv.NewIndexAtSource(util.SecsToTime(3), 1, 1, 1, 1, reservation.CorePath) - require.NoError(t, err) - _, err = rsv.NewIndexAtSource(util.SecsToTime(3), 1, 1, 1, 1, reservation.CorePath) - require.NoError(t, err) - return []*segment.Reservation{rsv} - }, - excludeID: "ff0000010001beefcafe", - }, - "one reservation but excluded": { - blockedBW: 0, - rsvsFcn: func() []*segment.Reservation { - rsv := testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5) - _, err := rsv.NewIndexAtSource(util.SecsToTime(3), 1, 1, 1, 1, reservation.CorePath) - require.NoError(t, err) - _, err = rsv.NewIndexAtSource(util.SecsToTime(3), 1, 1, 1, 1, reservation.CorePath) - require.NoError(t, err) - return []*segment.Reservation{rsv} - }, - excludeID: "ff0000010001beefcafe", - }, - "many reservations": { - blockedBW: 309, // 181 + 128 - rsvsFcn: func() []*segment.Reservation { - rsv := testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5) - _, err := rsv.NewIndexAtSource(util.SecsToTime(3), 1, 17, 7, 1, - reservation.CorePath) - require.NoError(t, err) - rsvs := []*segment.Reservation{rsv} - - rsv = testNewRsv(t, "ff00:1:1", "01234567", 1, 2, 5, 5, 5) - _, err = rsv.NewIndexAtSource(util.SecsToTime(3), 1, 8, 8, 1, reservation.CorePath) - require.NoError(t, err) - _, err = rsv.NewIndexAtSource(util.SecsToTime(3), 1, 7, 7, 1, reservation.CorePath) - require.NoError(t, err) - rsvs = append(rsvs, rsv) - - rsv = testNewRsv(t, "ff00:1:2", "01234567", 1, 2, 5, 5, 5) - _, err = rsv.NewIndexAtSource(util.SecsToTime(2), 1, 7, 7, 1, reservation.CorePath) - require.NoError(t, err) - rsvs = append(rsvs, rsv) - - return rsvs - }, - excludeID: "ff0000010001beefcafe", - }, - } - - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - excludedID, err := reservation.SegmentIDFromRaw(xtest.MustParseHexString(tc.excludeID)) - require.NoError(t, err) - sum := sumMaxBlockedBW(tc.rsvsFcn(), *excludedID) - require.Equal(t, tc.blockedBW, sum) - }) - } -} - -func TestAvailableBW(t *testing.T) { - req := newTestRequest(t, 1, 2, 5, 7) - - cases := map[string]struct { - availBW uint64 - delta float64 - req *segment.SetupReq - setupDB func(db *mock_backend.MockDB) - }{ - "empty DB": { - availBW: 1024, - delta: 1, - req: req, - setupDB: func(db *mock_backend.MockDB) { - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), &req.Ingress, nil).Return( - nil, nil) - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), nil, &req.Egress).Return( - nil, nil) - }, - }, - "this reservation in DB": { - // as the only reservation in DB has the same ID as the request, the availableBW - // function should return the same value as with an empty DB. - availBW: 1024, - delta: 1, - req: req, - setupDB: func(db *mock_backend.MockDB) { - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), &req.Ingress, nil).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), nil, &req.Egress).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - }, - }, - "other reservation in DB": { - availBW: 1024 - 64, - delta: 1, - req: req, - setupDB: func(db *mock_backend.MockDB) { - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), &req.Ingress, nil).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), nil, &req.Egress).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - }, - }, - "change delta": { - availBW: (1024 - 64) / 2, - delta: .5, - req: req, - setupDB: func(db *mock_backend.MockDB) { - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), &req.Ingress, nil).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - db.EXPECT().GetSegmentRsvsFromIFPair(gomock.Any(), nil, &req.Egress).Return( - []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "beefcafe", 1, 2, 5, 5, 5), - }, nil) - }, - }, - } - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - adm, finish := newTestAdmitter(t) - defer finish() - - adm.Delta = tc.delta - ctx := context.Background() - db := adm.DB.(*mock_backend.MockDB) - tc.setupDB(db) - avail, err := adm.availableBW(ctx, tc.req) - require.NoError(t, err) - require.Equal(t, tc.availBW, avail) - }) - } -} - -func TestTubeRatio(t *testing.T) { - cases := map[string]struct { - tubeRatio float64 - req *segment.SetupReq - setupDB func(db *mock_backend.MockDB) - globalCapacity uint64 - interfaces []uint16 - }{ - "empty": { - tubeRatio: 1, - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{} - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3}, - }, - "one source, one ingress": { - tubeRatio: 1, - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3}, - }, - "one source, two ingress": { - tubeRatio: .5, - req: newTestRequest(t, 1, 2, 3, 3), // 64Kbps - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 3, 3), // 64Kbps - testNewRsv(t, "ff00:1:1", "00000002", 3, 2, 5, 5, 5), // 128Kbps - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3}, - }, - "two sources, request already present": { - tubeRatio: .5, - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 9, 9), // will be ignored - testNewRsv(t, "ff00:1:1", "00000002", 3, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3}, - }, - "multiple sources, multiple ingress": { - tubeRatio: .75, - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:1", "00000002", 3, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3}, - }, - "exceeding ingress capacity": { - tubeRatio: 10. / 13., // 10 / (10 + 0 + 3) - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:1", "00000002", 3, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 10, - interfaces: []uint16{1, 2, 3}, - }, - "with many other irrelevant reservations": { - tubeRatio: .75, - req: newTestRequest(t, 1, 2, 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:2", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:1", "00000002", 3, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:3", "00000001", 4, 5, 5, 9, 9), - testNewRsv(t, "ff00:1:3", "00000002", 4, 5, 5, 9, 9), - testNewRsv(t, "ff00:1:4", "00000001", 5, 4, 5, 9, 9), - testNewRsv(t, "ff00:1:4", "00000002", 5, 4, 5, 9, 9), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - globalCapacity: 1024 * 1024, - interfaces: []uint16{1, 2, 3, 4, 5}, - }, - } - - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - adm, finish := newTestAdmitter(t) - defer finish() - - adm.Capacities = &testCapacities{ - Cap: tc.globalCapacity, - Ifaces: tc.interfaces, - } - db := adm.DB.(*mock_backend.MockDB) - tc.setupDB(db) - - ctx := context.Background() - demPerSrc, err := adm.computeTempDemands(ctx, tc.req.Ingress, tc.req) - require.NoError(t, err) - ratio, err := adm.tubeRatio(ctx, tc.req, demPerSrc) - require.NoError(t, err) - require.Equal(t, tc.tubeRatio, ratio) - }) - } -} - -func TestLinkRatio(t *testing.T) { - cases := map[string]struct { - linkRatio float64 - req *segment.SetupReq - setupDB func(db *mock_backend.MockDB) - }{ - "empty": { - linkRatio: 1., - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{} - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "same request": { - linkRatio: 1., - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "same source": { - linkRatio: .5, - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "beefcafe", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "different sources": { - linkRatio: 1. / 3., - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:2", "00000001", 1, 2, 5, 5, 5), - testNewRsv(t, "ff00:1:3", "00000001", 1, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "different egress interface": { - linkRatio: .5, - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 5, 5), - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 3, 5, 5, 5), - // testNewRsv(t, "ff00:1:3", "00000001", 1, 2, 5, 5, 5), - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "smaller prevBW": { - linkRatio: 1. / 3., - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 3, 3), // 64 Kbps - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), // 128 Kbps - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - "bigger prevBW": { - linkRatio: 2. / 3., - req: testAddAllocTrail(newTestRequest(t, 1, 2, 5, 5), 7, 7), // 256 Kbps - setupDB: func(db *mock_backend.MockDB) { - rsvs := []*segment.Reservation{ - testNewRsv(t, "ff00:1:1", "00000001", 1, 2, 5, 5, 5), // 128 Kbps - } - db.EXPECT().GetAllSegmentRsvs(gomock.Any()).AnyTimes().Return(rsvs, nil) - }, - }, - } - - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - adm, finish := newTestAdmitter(t) - defer finish() - - adm.Capacities = &testCapacities{ - Cap: 1024 * 1024, - Ifaces: []uint16{1, 2, 3}, - } - db := adm.DB.(*mock_backend.MockDB) - tc.setupDB(db) - - ctx := context.Background() - demsPerSrc, err := adm.computeTempDemands(ctx, tc.req.Ingress, tc.req) - require.NoError(t, err) - linkRatio, err := adm.linkRatio(ctx, tc.req, demsPerSrc) - require.NoError(t, err) - require.Equal(t, tc.linkRatio, linkRatio) - }) - } - -} - -type testCapacities struct { - Cap uint64 - Ifaces []uint16 -} - -var _ base.Capacities = (*testCapacities)(nil) - -func (c *testCapacities) IngressInterfaces() []uint16 { return c.Ifaces } -func (c *testCapacities) EgressInterfaces() []uint16 { return c.Ifaces } -func (c *testCapacities) Capacity(from, to uint16) uint64 { return c.Cap } -func (c *testCapacities) CapacityIngress(ingress uint16) uint64 { return c.Cap } -func (c *testCapacities) CapacityEgress(egress uint16) uint64 { return c.Cap } - -func newTestAdmitter(t *testing.T) (*StatelessAdmission, func()) { - mctlr := gomock.NewController(t) - - db := mock_backend.NewMockDB(mctlr) - return &StatelessAdmission{ - DB: db, - Capacities: &testCapacities{ - Cap: 1024, // 1MBps - Ifaces: []uint16{1, 2}, - }, - Delta: 1, - }, mctlr.Finish -} - -// newTestRequest creates a request ID ff00:1:1 beefcafe -func newTestRequest(t *testing.T, ingress, egress uint16, - minBW, maxBW reservation.BWCls) *segment.SetupReq { - - ID, err := reservation.SegmentIDFromRaw(xtest.MustParseHexString("ff0000010001beefcafe")) - require.NoError(t, err) - return &segment.SetupReq{ - Request: segment.Request{ - RequestMetadata: base.RequestMetadata{}, - ID: *ID, - Timestamp: util.SecsToTime(1), - Ingress: ingress, - Egress: egress, - }, - MinBW: minBW, - MaxBW: maxBW, - SplitCls: 2, - PathProps: reservation.StartLocal | reservation.EndLocal, - } -} - -func testNewRsv(t *testing.T, srcAS string, suffix string, ingress, egress uint16, - minBW, maxBW, allocBW reservation.BWCls) *segment.Reservation { - - ID, err := reservation.NewSegmentID(xtest.MustParseAS(srcAS), - xtest.MustParseHexString(suffix)) - require.NoError(t, err) - rsv := &segment.Reservation{ - ID: *ID, - Indices: segment.Indices{ - segment.Index{ - Idx: 10, - Expiration: util.SecsToTime(2), - MinBW: minBW, - MaxBW: maxBW, - AllocBW: allocBW, - }, - }, - Ingress: ingress, - Egress: egress, - PathType: reservation.UpPath, - PathEndProps: reservation.StartLocal | reservation.EndLocal | reservation.EndTransfer, - TrafficSplit: 2, - } - err = rsv.SetIndexConfirmed(10) - require.NoError(t, err) - err = rsv.SetIndexActive(10) - require.NoError(t, err) - return rsv -} - -// testAddAllocTrail adds an allocation trail to a reservation. The beads parameter represents -// the trail like: alloc0,max0,alloc1,max1,... -func testAddAllocTrail(req *segment.SetupReq, beads ...reservation.BWCls) *segment.SetupReq { - if len(beads)%2 != 0 { - panic("the beads must be even") - } - for i := 0; i < len(beads); i += 2 { - beads := reservation.AllocationBead{ - AllocBW: beads[i], - MaxBW: beads[i+1], - } - req.AllocTrail = append(req.AllocTrail, beads) - } - return req -} diff --git a/control/colibri/reservation/segment/export_test.go b/control/colibri/reservation/segment/export_test.go deleted file mode 100644 index 08b8261293..0000000000 --- a/control/colibri/reservation/segment/export_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -func (r *Reservation) GetActiveIndexForTesting() int { - return r.activeIndex -} -func (r *Reservation) SetActiveIndexForTesting(index int) { - r.activeIndex = index -} - -func (index *Index) SetStateForTesting(state IndexState) { - index.state = state -} diff --git a/control/colibri/reservation/segment/index.go b/control/colibri/reservation/segment/index.go deleted file mode 100644 index 02e52bf82f..0000000000 --- a/control/colibri/reservation/segment/index.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" -) - -type IndexState uint8 - -// possible states of a segment reservation index. -const ( - IndexTemporary IndexState = iota - IndexPending // the index is confirmed, but not yet activated. - IndexActive -) - -// Index is a segment reservation index. -type Index struct { - Idx reservation.IndexNumber - Expiration time.Time - state IndexState - MinBW reservation.BWCls - MaxBW reservation.BWCls - AllocBW reservation.BWCls - Token *reservation.Token -} - -// NewIndex creates a new Index without yet linking it to any reservation. -func NewIndex(idx reservation.IndexNumber, expiration time.Time, state IndexState, - minBW, maxBW, allocBW reservation.BWCls, token *reservation.Token) *Index { - return &Index{ - Idx: idx, - Expiration: expiration, - state: state, - MinBW: minBW, - MaxBW: maxBW, - AllocBW: allocBW, - Token: token, - } -} - -// State returns the read-only state. -func (index *Index) State() IndexState { - return index.state -} - -// Indices is a collection of Index that implements IndicesInterface. -type Indices []Index - -var _ base.IndicesInterface = (*Indices)(nil) - -func (idxs Indices) Len() int { return len(idxs) } -func (idxs Indices) GetIndexNumber(i int) reservation.IndexNumber { return idxs[i].Idx } -func (idxs Indices) GetExpiration(i int) time.Time { return idxs[i].Expiration } -func (idxs Indices) GetAllocBW(i int) reservation.BWCls { return idxs[i].AllocBW } -func (idxs Indices) GetToken(i int) *reservation.Token { return idxs[i].Token } -func (idxs Indices) Rotate(i int) base.IndicesInterface { - return append(idxs[i:], idxs[:i]...) -} diff --git a/control/colibri/reservation/segment/path.go b/control/colibri/reservation/segment/path.go deleted file mode 100644 index a2eb8afece..0000000000 --- a/control/colibri/reservation/segment/path.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -import ( - "encoding/binary" - "fmt" - "io" - "strings" - - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// ReservationTransparentPath represents a reservation path, in the reservation order. -// This path is seen only in the source of a segment reservation. -// TODO(juagargi) there exists a ColibriPath that could be used instead, if we only -// need equality. If we need to know the IDs of the transit ASes, it won't be possible. -type ReservationTransparentPath []PathStepWithIA - -var _ io.Reader = (*ReservationTransparentPath)(nil) - -// NewPathFromRaw constructs a new Path from the byte representation. -func NewPathFromRaw(buff []byte) (ReservationTransparentPath, error) { - if len(buff)%PathStepWithIALen != 0 { - return nil, serrors.New("buffer input is not a multiple of a path step", "len", len(buff)) - } - steps := len(buff) / PathStepWithIALen - p := make(ReservationTransparentPath, steps) - for i := 0; i < steps; i++ { - offset := i * PathStepWithIALen - p[i].Ingress = binary.BigEndian.Uint16(buff[offset:]) - p[i].Egress = binary.BigEndian.Uint16(buff[offset+2:]) - p[i].IA = addr.IA(binary.BigEndian.Uint64(buff[offset+4:])) - } - return p, nil -} - -// Validate returns an error if there is invalid data. -func (p ReservationTransparentPath) Validate() error { - if len(p) < 2 { - return serrors.New("invalid path length", "len", len(p)) - } - if p[0].Ingress != 0 { - return serrors.New("wrong ingress interface for source", "ingress", p[0].Ingress) - } - if p[len(p)-1].Egress != 0 { - return serrors.New("wrong egress interface for destination", - "egress ID", p[len(p)-1].Ingress) - } - return nil -} - -// Equal returns true if both ReservationTransparentPath contain the same values. -func (p ReservationTransparentPath) Equal(o ReservationTransparentPath) bool { - if len(p) != len(o) { - return false - } - for i := 0; i < len(p); i++ { - if p[i] != o[i] { - return false - } - } - return true -} - -// GetSrcIA returns the source IA in the path or a zero IA if the path is nil (it's not the -// source AS of the reservation and has no access to the path of the reservation). -// If the Path is not nil, it assumes is valid, i.e. it has at least length 2. -func (p ReservationTransparentPath) GetSrcIA() addr.IA { - if len(p) == 0 { - return 0 - } - return p[0].IA -} - -// GetDstIA returns the source IA in the path or a zero IA if the path is nil (it's not the -// source AS of the reservation and has no access to the path of the reservation). -// If the path is not nil, it assumes is valid, i.e. it has at least length 2. -func (p ReservationTransparentPath) GetDstIA() addr.IA { - if len(p) == 0 { - return 0 - } - return p[len(p)-1].IA -} - -// Len returns the length of this path in bytes, when serialized. -func (p ReservationTransparentPath) Len() int { - if len(p) == 0 { - return 0 - } - return len(p) * PathStepWithIALen -} - -func (p ReservationTransparentPath) Read(buff []byte) (int, error) { - if len(p) == 0 { - return 0, nil - } - if len(buff) < p.Len() { - return 0, serrors.New("buffer too small", "min_size", p.Len(), "actual_size", len(buff)) - } - for i, s := range p { - offset := i * PathStepWithIALen - binary.BigEndian.PutUint16(buff[offset:], s.Ingress) - binary.BigEndian.PutUint16(buff[offset+2:], s.Egress) - binary.BigEndian.PutUint64(buff[offset+4:], uint64(s.IA)) - } - return p.Len(), nil -} - -// ToRaw returns a buffer representing this ReservationTransparentPath. -func (p ReservationTransparentPath) ToRaw() []byte { - if len(p) == 0 { - return nil - } - buff := make([]byte, p.Len()) - _, _ = p.Read(buff) - return buff -} - -func (p ReservationTransparentPath) String() string { - strs := make([]string, len(p)) - for i, s := range p { - strs[i] = s.String() - } - return strings.Join(strs, ">") -} - -// PathStep is one hop of the ReservationTransparentPath. -// For a source AS Ingress will be invalid. Conversely for dst. -type PathStep struct { - Ingress uint16 - Egress uint16 -} - -// PathStepWithIA is a step in a reservation path as seen from the source AS. -type PathStepWithIA struct { - PathStep - IA addr.IA -} - -// PathStepWithIALen amounts for Ingress+Egress+IA. -const PathStepWithIALen = 2 + 2 + 8 - -func (s *PathStepWithIA) String() string { - return fmt.Sprintf("%d %s %d", s.Ingress, s.IA.String(), s.Egress) -} diff --git a/control/colibri/reservation/segment/path_test.go b/control/colibri/reservation/segment/path_test.go deleted file mode 100644 index cbfa735340..0000000000 --- a/control/colibri/reservation/segment/path_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segmenttest" - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestValidatePath(t *testing.T) { - tc := map[string]struct { - Path segment.ReservationTransparentPath - IsValid bool - }{ - "src-dst": { - Path: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - IsValid: true, - }, - "invalid dst": { - Path: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 2), - IsValid: false, - }, - "invalid src": { - Path: segmenttest.NewPathFromComponents(2, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - IsValid: false, - }, - } - for name, tc := range tc { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - err := tc.Path.Validate() - if tc.IsValid { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} - -func TestEqualPath(t *testing.T) { - tc := map[string]struct { - Path1 segment.ReservationTransparentPath - Path2 segment.ReservationTransparentPath - IsEqual bool - }{ - "eq1": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - IsEqual: true, - }, - "eq2": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 2, "1-ff00:1:10", 3, - 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 2, "1-ff00:1:10", 3, - 1, "1-ff00:0:2", 0), - IsEqual: true, - }, - "eq3": { - Path1: nil, - Path2: nil, - IsEqual: true, - }, - "eq4": { - Path1: nil, - Path2: make(segment.ReservationTransparentPath, 0), - IsEqual: true, - }, - "neq1": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(1, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - IsEqual: false, - }, - "neq2": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(0, "1-ff00:0:3", 1, 1, "1-ff00:0:2", 0), - IsEqual: false, - }, - "neq3": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 2, 1, "1-ff00:0:2", 0), - IsEqual: false, - }, - "neq4": { - Path1: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 2, "1-ff00:1:10", 3, - 1, "1-ff00:0:2", 0), - Path2: segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 2, "1-ff00:1:10", 3), - IsEqual: false, - }, - } - for name, tc := range tc { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - eq := tc.Path1.Equal(tc.Path2) - require.Equal(t, tc.IsEqual, eq) - }) - } -} - -func TestGetIAs(t *testing.T) { - p := segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - require.Equal(t, xtest.MustParseIA("1-ff00:0:1"), p.GetSrcIA()) - require.Equal(t, xtest.MustParseIA("1-ff00:0:2"), p.GetDstIA()) - p = nil - require.Equal(t, xtest.MustParseIA("0-0"), p.GetSrcIA()) - require.Equal(t, xtest.MustParseIA("0-0"), p.GetDstIA()) - p = make(segment.ReservationTransparentPath, 0) - require.Equal(t, xtest.MustParseIA("0-0"), p.GetSrcIA()) - require.Equal(t, xtest.MustParseIA("0-0"), p.GetDstIA()) -} - -func TestPathLen(t *testing.T) { - p := segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - require.Equal(t, 2*12, p.Len()) - p = segment.ReservationTransparentPath{} - require.Equal(t, 0, p.Len()) - p = nil - require.Equal(t, 0, p.Len()) - p = make(segment.ReservationTransparentPath, 0) - require.Equal(t, 0, p.Len()) -} - -func TestToFromBinary(t *testing.T) { - p := segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - var buff []byte - _, err := p.Read(buff) - require.Error(t, err) - _, err = p.Read(buff) - require.Error(t, err) - buff = make([]byte, 2*12) - c, err := p.Read(buff) - require.NoError(t, err) - require.Equal(t, 2*12, c) - - anotherP, err := segment.NewPathFromRaw(buff) - require.NoError(t, err) - require.Equal(t, p, anotherP) - - anotherBuff := p.ToRaw() - require.Equal(t, buff, anotherBuff) - // wrong buffer - buff = buff[:len(buff)-1] - _, err = segment.NewPathFromRaw(buff) - require.Error(t, err) - // empty and nil buffer - p, err = segment.NewPathFromRaw(nil) - require.NoError(t, err) - require.Empty(t, p) - p, err = segment.NewPathFromRaw([]byte{}) - require.NoError(t, err) - require.Empty(t, p) - // empty and nil path - p = nil - require.Empty(t, p.ToRaw()) - p = make(segment.ReservationTransparentPath, 0) - require.Empty(t, p.ToRaw()) -} - -func TestString(t *testing.T) { - p := segmenttest.NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - require.Equal(t, "0 1-ff00:0:1 1>1 1-ff00:0:2 0", p.String()) -} diff --git a/control/colibri/reservation/segment/request.go b/control/colibri/reservation/segment/request.go deleted file mode 100644 index a3a132d991..0000000000 --- a/control/colibri/reservation/segment/request.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Request is the base struct for any type of COLIBRI segment request. -// It contains a reference to the reservation it requests, or nil if not yet created. -type Request struct { - base.RequestMetadata // information about the request (forwarding path) - ID reservation.SegmentID // the ID this request refers to - Index reservation.IndexNumber // the index this request refers to - Timestamp time.Time // the mandatory timestamp - Ingress uint16 // the interface the traffic uses to enter the AS - Egress uint16 // the interface the traffic uses to leave the AS - Reservation *Reservation // nil if no reservation yet -} - -// NewRequest constructs the segment Request type. -func NewRequest(ts time.Time, id *reservation.SegmentID, idx reservation.IndexNumber, - path base.ColibriPath) (*Request, error) { - - metadata, err := base.NewRequestMetadata(path) - if err != nil { - return nil, serrors.WrapStr("new segment request", err) - } - ingressIFID, egressIFID := path.IngressEgressIFIDs() - if id == nil { - return nil, serrors.New("new segment request with nil ID") - } - return &Request{ - RequestMetadata: *metadata, - Timestamp: ts, - ID: *id, - Index: idx, - Ingress: ingressIFID, - Egress: egressIFID, - }, nil -} - -// SetupReq is a segment reservation setup request. -// This same type is used for renewal of the segment reservation. -type SetupReq struct { - Request - InfoField reservation.InfoField - MinBW reservation.BWCls - MaxBW reservation.BWCls - SplitCls reservation.SplitCls - PathProps reservation.PathEndProps - AllocTrail reservation.AllocationBeads -} - -// SetupTelesReq represents a telescopic segment setup. -type SetupTelesReq struct { - SetupReq - BaseID reservation.SegmentID -} - -// TeardownReq requests the AS to remove a given index from the DB. If this is the last index -// in the reservation, the reservation will be completely removed. -type TeardownReq struct { - Request -} - -// IndexConfirmationReq is used to change the state on an index (e.g. from temporary to pending). -type IndexConfirmationReq struct { - Request - State IndexState -} - -// CleanupReq is used to clean an index. -type CleanupReq struct { - Request -} diff --git a/control/colibri/reservation/segment/reservation.go b/control/colibri/reservation/segment/reservation.go deleted file mode 100644 index 0c9faad4de..0000000000 --- a/control/colibri/reservation/segment/reservation.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Reservation represents a segment reservation. -type Reservation struct { - ID reservation.SegmentID - Indices Indices // existing indices in this reservation - activeIndex int // -1 <= activeIndex < len(Indices) - Ingress uint16 // igress interface ID: reservation packets enter - Egress uint16 // egress interface ID: reservation packets leave - Path ReservationTransparentPath // empty if not at the source of the reservation - PathType reservation.PathType // the type of path (up,core,down) - PathEndProps reservation.PathEndProps // the properties for stitching and start/end - TrafficSplit reservation.SplitCls // the traffic split between control and data planes -} - -func NewReservation() *Reservation { - return &Reservation{ - activeIndex: -1, - } -} - -// Validate will return an error for invalid values. -func (r *Reservation) Validate() error { - if r.ID.ASID == 0 { - return serrors.New("Reservation ID not set") - } - if err := base.ValidateIndices(r.Indices); err != nil { - return err - } - if r.activeIndex < -1 || r.activeIndex > 0 || r.activeIndex >= len(r.Indices) { - // when we activate an index all previous indices are removed. - // Thus activeIndex can only be -1 or 0 - return serrors.New("invalid active index", "active_index", r.activeIndex) - } - activeIndex := -1 - for i, index := range r.Indices { - if index.State() == IndexActive { - if activeIndex != -1 { - return serrors.New("more than one active index", - "first_active", r.Indices[activeIndex].Idx, "another_active", index.Idx) - } - activeIndex = i - } - } - var err error - if r.Path != nil { - if r.Ingress != 0 { - return serrors.New("reservation starts in this AS but ingress interface is not zero", - "ingress_if", r.Ingress) - } - err = r.Path.Validate() - } else if r.Ingress == 0 { - return serrors.New("reservation does not start in this AS but ingress interface is zero") - } - if err != nil { - return serrors.WrapStr("validating reservation, path failed", err) - } - err = r.PathEndProps.Validate() - if err != nil { - return serrors.WrapStr("validating reservation, end properties failed", err) - } - return nil -} - -// ActiveIndex returns the currently active Index for this reservation, or nil if none. -func (r *Reservation) ActiveIndex() *Index { - if r.activeIndex == -1 { - return nil - } - return &r.Indices[r.activeIndex] -} - -// NewIndexAtSource creates a new index. The associated token is created from the arguments, and -// automatically linked to the index. This function should be called only from the -// AS originating the reservation. -// The expiration times must always be greater or equal than those in previous indices. -func (r *Reservation) NewIndexAtSource(expTime time.Time, minBW, maxBW, allocBW reservation.BWCls, - rlc reservation.RLC, pathType reservation.PathType) (reservation.IndexNumber, error) { - - idx := reservation.IndexNumber(0) - if len(r.Indices) > 0 { - idx = r.Indices[len(r.Indices)-1].Idx.Add(1) - } - tok := &reservation.Token{ - InfoField: reservation.InfoField{ - Idx: idx, - ExpirationTick: reservation.TickFromTime(expTime), - BWCls: allocBW, - RLC: rlc, - PathType: pathType, - }, - } - index := NewIndex(idx, expTime, IndexTemporary, minBW, maxBW, allocBW, tok) - return r.addIndex(index) -} - -// NewIndexFromToken creates a new index. The token argument is used to populate several -// fields of the index. The token is not stored (on-path ASes don't need the token). -// This function should be called from an AS that is on the reservation path -// but not the originating one. -func (r *Reservation) NewIndexFromToken(tok *reservation.Token, minBW, maxBW reservation.BWCls) ( - reservation.IndexNumber, error) { - - if tok == nil { - return 0, serrors.New("token is nil") - } - index := NewIndex(tok.Idx, tok.ExpirationTick.ToTime(), IndexTemporary, minBW, maxBW, - tok.BWCls, nil) - return r.addIndex(index) -} - -func (r *Reservation) addIndex(index *Index) (reservation.IndexNumber, error) { - newIndices := make(Indices, len(r.Indices)+1) - copy(newIndices, r.Indices) - newIndices[len(newIndices)-1] = *index - if err := base.ValidateIndices(newIndices); err != nil { - return 0, err - } - r.Indices = newIndices - return index.Idx, nil -} - -// Index finds the Index with that IndexNumber and returns a pointer to it. -func (r *Reservation) Index(idx reservation.IndexNumber) *Index { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return nil - } - return &r.Indices[sliceIndex] -} - -// SetIndexConfirmed sets the index as IndexPending (confirmed but not active). If the requested -// index has state active, it will emit an error. -func (r *Reservation) SetIndexConfirmed(idx reservation.IndexNumber) error { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return err - } - if r.Indices[sliceIndex].state == IndexActive { - return serrors.New("cannot confirm an already active index", "index_number", idx) - } - r.Indices[sliceIndex].state = IndexPending - return nil -} - -// SetIndexActive sets the index as active. If the reservation had already an active state, -// it will remove all previous indices. -func (r *Reservation) SetIndexActive(idx reservation.IndexNumber) error { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return err - } - if r.activeIndex == sliceIndex { - return nil // already active - } - // valid states are Pending (nominal) and Active (reconstructing from DB needs this) - if r.Indices[sliceIndex].state != IndexPending && r.Indices[sliceIndex].state != IndexActive { - return serrors.New("attempt to activate a non confirmed index", "index_number", idx, - "state", r.Indices[sliceIndex].state) - } - if r.activeIndex > -1 { - if r.activeIndex > sliceIndex { - return serrors.New("activating a past index", - "last active", r.Indices[r.activeIndex].Idx, "current", idx) - } - } - // remove indices [lastActive,currActive) so that currActive is at position 0 - r.Indices = r.Indices[sliceIndex:] - r.activeIndex = 0 - r.Indices[0].state = IndexActive - return nil -} - -// RemoveIndex removes all indices from the beginning until this one, inclusive. -func (r *Reservation) RemoveIndex(idx reservation.IndexNumber) error { - sliceIndex, err := base.FindIndex(r.Indices, idx) - if err != nil { - return err - } - r.Indices = r.Indices[sliceIndex+1:] - r.activeIndex -= sliceIndex - if r.activeIndex < -1 { - r.activeIndex = -1 - } - return nil -} - -// MaxBlockedBW returns the maximum bandwidth blocked by this reservation, which is -// the same as the maximum allocated bandwidth indicated by its indices. -func (r *Reservation) MaxBlockedBW() uint64 { - if len(r.Indices) == 0 { - return 0 - } - var max reservation.BWCls - for _, idx := range r.Indices { - max = reservation.MaxBWCls(max, idx.AllocBW) - } - return max.ToKbps() -} - -// MaxRequestedBW returns the maximum bandwidth requested by this reservation. -func (r *Reservation) MaxRequestedBW() uint64 { - if len(r.Indices) == 0 { - return 0 - } - var max reservation.BWCls - for _, idx := range r.Indices { - max = reservation.MaxBWCls(max, idx.MaxBW) - } - return max.ToKbps() -} diff --git a/control/colibri/reservation/segment/reservation_test.go b/control/colibri/reservation/segment/reservation_test.go deleted file mode 100644 index 22c907f3b4..0000000000 --- a/control/colibri/reservation/segment/reservation_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segmenttest" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/util" -) - -func TestNewIndexAtSource(t *testing.T) { - r := segmenttest.NewReservation() - require.Len(t, r.Indices, 0) - expTime := util.SecsToTime(1) - idx, err := r.NewIndexAtSource(expTime, 1, 3, 2, 5, reservation.CorePath) - require.NoError(t, err) - require.Len(t, r.Indices, 1) - require.Equal(t, reservation.IndexNumber(0), idx) - require.Equal(t, idx, r.Indices[0].Idx) - require.Equal(t, expTime, r.Indices[0].Expiration) - require.Equal(t, segment.IndexTemporary, r.Indices[0].State()) - require.Equal(t, reservation.BWCls(1), r.Indices[0].MinBW) - require.Equal(t, reservation.BWCls(3), r.Indices[0].MaxBW) - require.Equal(t, reservation.BWCls(2), r.Indices[0].AllocBW) - require.NotNil(t, r.Indices[0].Token) - tok := &reservation.Token{ - InfoField: reservation.InfoField{ - ExpirationTick: reservation.TickFromTime(expTime), - BWCls: 2, - RLC: 5, - Idx: idx, - PathType: reservation.CorePath, - }, - } - require.Equal(t, tok, r.Indices[0].Token) - // add a second index - idx, err = r.NewIndexAtSource(expTime, 1, 3, 2, 5, reservation.CorePath) - require.NoError(t, err) - require.Len(t, r.Indices, 2) - require.Equal(t, reservation.IndexNumber(1), idx) - require.Equal(t, idx, r.Indices[1].Idx) - // remove first index and add another one - r.Indices = r.Indices[1:] - idx, err = r.NewIndexAtSource(expTime, 1, 3, 2, 5, reservation.CorePath) - require.NoError(t, err) - require.Len(t, r.Indices, 2) - require.Equal(t, reservation.IndexNumber(2), idx) - require.Equal(t, idx, r.Indices[1].Idx) -} - -func TestNewIndexFromToken(t *testing.T) { - r := segmenttest.NewReservation() - require.Len(t, r.Indices, 0) - expTime := time.Unix(1, 0) - tok := &reservation.Token{ - InfoField: reservation.InfoField{ - ExpirationTick: reservation.TickFromTime(expTime), - BWCls: 2, - RLC: 5, - Idx: reservation.IndexNumber(6), - PathType: reservation.CorePath, - }, - } - idx, err := r.NewIndexFromToken(tok, 1, 3) - require.NoError(t, err) - require.Equal(t, tok.Idx, idx) - require.Equal(t, tok.ExpirationTick.ToTime(), r.Indices[0].Expiration) - require.Equal(t, segment.IndexTemporary, r.Indices[0].State()) - require.Equal(t, reservation.BWCls(1), r.Indices[0].MinBW) - require.Equal(t, reservation.BWCls(3), r.Indices[0].MaxBW) - require.Equal(t, tok.BWCls, r.Indices[0].AllocBW) - require.Nil(t, r.Indices[0].Token) - // nil token - _, err = r.NewIndexFromToken(nil, 0, 0) - require.Error(t, err) -} - -func TestReservationValidate(t *testing.T) { - r := segmenttest.NewReservation() - err := r.Validate() - require.NoError(t, err) - // wrong path - r.Path = segment.ReservationTransparentPath{} - err = r.Validate() - require.Error(t, err) - // more than one active index - expTime := util.SecsToTime(1) - r = segmenttest.NewReservation() - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.Len(t, r.Indices, 2) - r.Indices[0].SetStateForTesting(segment.IndexActive) - r.Indices[1].SetStateForTesting(segment.IndexActive) - err = r.Validate() - require.Error(t, err) - // ID not set - r = segmenttest.NewReservation() - r.ID = reservation.SegmentID{} - err = r.Validate() - require.Error(t, err) - // starts in this AS but ingress nonzero - r = segmenttest.NewReservation() - r.Ingress = 1 - err = r.Validate() - require.Error(t, err) - // Does not start in this AS but ingress empty - r = segmenttest.NewReservation() - r.Path = nil - err = r.Validate() - require.Error(t, err) -} - -func TestIndex(t *testing.T) { - r := segmenttest.NewReservation() - expTime := util.SecsToTime(1) - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - idx, _ := r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.Len(t, r.Indices, 3) - index := r.Index(idx) - require.Equal(t, &r.Indices[1], index) - index = r.Index(reservation.IndexNumber(4)) - require.Nil(t, index) - r.SetIndexConfirmed(idx) - r.SetIndexActive(idx) - index = r.Index(idx) - require.Equal(t, &r.Indices[0], index) -} - -func TestSetIndexConfirmed(t *testing.T) { - r := segmenttest.NewReservation() - expTime := util.SecsToTime(1) - id, _ := r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.Equal(t, segment.IndexTemporary, r.Indices[0].State()) - err := r.SetIndexConfirmed(id) - require.NoError(t, err) - require.Equal(t, segment.IndexPending, r.Indices[0].State()) - - // confirm already confirmed - err = r.SetIndexConfirmed(id) - require.NoError(t, err) - require.Equal(t, segment.IndexPending, r.Indices[0].State()) -} - -func TestSetIndexActive(t *testing.T) { - r := segmenttest.NewReservation() - expTime := util.SecsToTime(1) - - // index not confirmed - idx, _ := r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - err := r.SetIndexActive(idx) - require.Error(t, err) - - // normal activation - r.SetIndexConfirmed(idx) - err = r.SetIndexActive(idx) - require.NoError(t, err) - require.Equal(t, segment.IndexActive, r.Indices[0].State()) - require.Equal(t, 0, r.GetActiveIndexForTesting()) - - // already active - err = r.SetIndexActive(idx) - require.NoError(t, err) - - // remove previous indices - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - idx, _ = r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.Len(t, r.Indices, 3) - require.Equal(t, 0, r.GetActiveIndexForTesting()) - r.SetIndexConfirmed(idx) - err = r.SetIndexActive(idx) - require.NoError(t, err) - require.Len(t, r.Indices, 1) - require.Equal(t, 0, r.GetActiveIndexForTesting()) - require.True(t, r.Indices[0].Idx == idx) -} - -func TestRemoveIndex(t *testing.T) { - r := segmenttest.NewReservation() - expTime := util.SecsToTime(1) - idx, _ := r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - err := r.RemoveIndex(idx) - require.NoError(t, err) - require.Len(t, r.Indices, 0) - - // remove second index - idx, _ = r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - idx2, _ := r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - err = r.RemoveIndex(idx) - require.NoError(t, err) - require.Len(t, r.Indices, 1) - require.True(t, r.Indices[0].Idx == idx2) - err = r.Validate() - require.NoError(t, err) - - // remove also removes older indices - expTime = expTime.Add(time.Second) - r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - idx, _ = r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - idx2, _ = r.NewIndexAtSource(expTime, 0, 0, 0, 0, reservation.CorePath) - require.Len(t, r.Indices, 4) - err = r.RemoveIndex(idx) - require.NoError(t, err) - require.Len(t, r.Indices, 1) - require.True(t, r.Indices[0].Idx == idx2) - err = r.Validate() - require.NoError(t, err) -} - -func TestMaxBlockedBW(t *testing.T) { - r := segmenttest.NewReservation() - r.Indices = r.Indices[:0] - require.Equal(t, uint64(0), r.MaxBlockedBW()) - r.NewIndexAtSource(util.SecsToTime(1), 1, 1, 1, 1, reservation.CorePath) - require.Equal(t, reservation.BWCls(1).ToKbps(), r.MaxBlockedBW()) - r.NewIndexAtSource(util.SecsToTime(1), 1, 1, 1, 1, reservation.CorePath) - require.Equal(t, reservation.BWCls(1).ToKbps(), r.MaxBlockedBW()) - r.Indices[0].AllocBW = 11 - require.Equal(t, reservation.BWCls(11).ToKbps(), r.MaxBlockedBW()) -} diff --git a/control/colibri/reservation/segment/response.go b/control/colibri/reservation/segment/response.go deleted file mode 100644 index 809f62906b..0000000000 --- a/control/colibri/reservation/segment/response.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segment - -import ( - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Response is the base struct for any type of COLIBRI segment response. -type Response struct { - base.RequestMetadata // information about the request (forwarding path) - ID reservation.SegmentID // the ID this request refers to - Index reservation.IndexNumber // the index this request refers to - Accepted bool // success or failure type of response - FailedHop uint8 // if accepted is false, the AS that failed it -} - -var _ base.MessageWithPath = (*Response)(nil) - -// NewResponse contructs the segment Response type. -func NewResponse(ts time.Time, id *reservation.SegmentID, idx reservation.IndexNumber, - path base.ColibriPath, accepted bool, failedHop uint8) (*Response, error) { - - metadata, err := base.NewRequestMetadata(path) - if err != nil { - return nil, serrors.WrapStr("new segment request", err) - } - if id == nil { - return nil, serrors.New("new segment response with nil ID") - } - return &Response{ - RequestMetadata: *metadata, - ID: *id, - Index: idx, - Accepted: accepted, - FailedHop: failedHop, - }, nil -} - -// ResponseSetupSuccess is the response to a success setup. It's sent on the reverse direction. -type ResponseSetupSuccess struct { - Response - Token reservation.Token -} - -// ResponseSetupFailure is the response to a failed setup. It's sent on the reverse direction. -type ResponseSetupFailure struct { - Response - FailedSetup *SetupReq -} - -// ResponseTeardownSuccess is sent by the last AS in the reverse path. -type ResponseTeardownSuccess struct { - Response -} - -// ResponseTeardownFailure is sent in the reverse path. -type ResponseTeardownFailure struct { - Response - ErrorCode uint8 -} - -// ResponseIndexConfirmationSuccess is a successful index confirmation. The target state is -// echoed in the response. -type ResponseIndexConfirmationSuccess struct { - Response - State IndexState -} - -// ResponseIndexConfirmationFailure is a failed index confirmation. -type ResponseIndexConfirmationFailure struct { - Response - ErrorCode uint8 -} - -// ResponseCleanupSuccess is a response to a successful cleanup request. -type ResponseCleanupSuccess struct { - Response -} - -// ResponseCleanupFailure is a failed index cleanup. -type ResponseCleanupFailure struct { - Response - ErrorCode uint8 -} diff --git a/control/colibri/reservation/segmenttest/BUILD.bazel b/control/colibri/reservation/segmenttest/BUILD.bazel deleted file mode 100644 index 9fb8c46299..0000000000 --- a/control/colibri/reservation/segmenttest/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["common.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/segmenttest", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation/segment:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/xtest:go_default_library", - ], -) diff --git a/control/colibri/reservation/segmenttest/common.go b/control/colibri/reservation/segmenttest/common.go deleted file mode 100644 index 75409d74da..0000000000 --- a/control/colibri/reservation/segmenttest/common.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 segmenttest - -import ( - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/xtest" -) - -func NewPathFromComponents(chain ...interface{}) segment.ReservationTransparentPath { - if len(chain)%3 != 0 { - panic("wrong number of arguments") - } - p := segment.ReservationTransparentPath{} - for i := 0; i < len(chain); i += 3 { - p = append(p, segment.PathStepWithIA{ - PathStep: segment.PathStep{ - Ingress: uint16(chain[i].(int)), - Egress: uint16(chain[i+2].(int)), - }, - IA: xtest.MustParseIA(chain[i+1].(string)), - }) - } - return p -} - -func NewReservation() *segment.Reservation { - segID, err := reservation.NewSegmentID(xtest.MustParseAS("ff00:0:1"), - xtest.MustParseHexString("beefcafe")) - if err != nil { - panic(err) - } - r := segment.NewReservation() - r.ID = *segID - r.Path = NewPathFromComponents(0, "1-ff00:0:1", 1, 1, "1-ff00:0:2", 0) - return r -} diff --git a/control/colibri/reservation/sqlite/BUILD.bazel b/control/colibri/reservation/sqlite/BUILD.bazel deleted file mode 100644 index a9cef9dc8a..0000000000 --- a/control/colibri/reservation/sqlite/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "db.go", - "schema.go", - ], - importpath = "github.com/scionproto/scion/control/colibri/reservation/sqlite", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservationstorage/backend:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - "//pkg/private/util:go_default_library", - "//private/storage/db:go_default_library", - "@com_github_mattn_go_sqlite3//:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["db_test.go"], - embed = [":go_default_library"], - deps = [ - "//control/colibri/reservation/reservationdbtest:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/xtest:go_default_library", - "@com_github_mattn_go_sqlite3//:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/control/colibri/reservation/sqlite/db.go b/control/colibri/reservation/sqlite/db.go deleted file mode 100644 index 6af562de28..0000000000 --- a/control/colibri/reservation/sqlite/db.go +++ /dev/null @@ -1,809 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 sqlite - -import ( - "context" - "database/sql" - "encoding/binary" - "encoding/hex" - "fmt" - "strings" - "sync" - "time" - - "github.com/mattn/go-sqlite3" - _ "github.com/mattn/go-sqlite3" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/e2e" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservationstorage/backend" - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" - "github.com/scionproto/scion/pkg/private/util" - "github.com/scionproto/scion/private/storage/db" -) - -type Backend struct { - *executor - db *sql.DB -} - -var _ backend.DB = (*Backend)(nil) - -// New returns a new SQLite backend opening a database at the given path. If -// no database exists a new database is be created. If the schema version of the -// stored database is different from the one in schema.go, an error is returned. -func New(path string) (*Backend, error) { - db, err := db.NewSqlite(path, Schema, SchemaVersion) - if err != nil { - return nil, err - } - return &Backend{ - executor: &executor{ - db: db, - }, - db: db, - }, nil -} - -// SetMaxOpenConns sets the maximum number of open connections. -func (b *Backend) SetMaxOpenConns(maxOpenConns int) { - b.db.SetMaxOpenConns(maxOpenConns) -} - -// SetMaxIdleConns sets the maximum number of idle connections. -func (b *Backend) SetMaxIdleConns(maxIdleConns int) { - b.db.SetMaxIdleConns(maxIdleConns) -} - -// BeginTransaction begins a transaction on the database. -func (b *Backend) BeginTransaction(ctx context.Context, opts *sql.TxOptions) ( - backend.Transaction, error) { - - b.Lock() - defer b.Unlock() - tx, err := b.db.BeginTx(ctx, opts) - if err != nil { - return nil, db.NewTxError("create tx", err) - } - return &transaction{ - executor: &executor{ - db: tx, - }, - tx: tx, - }, nil -} - -// Close closes the databse. -func (b *Backend) Close() error { - return b.db.Close() -} - -type transaction struct { - *executor - tx *sql.Tx -} - -var _ backend.Transaction = (*transaction)(nil) - -func (t *transaction) Commit() error { - t.Lock() - defer t.Unlock() - return t.tx.Commit() -} - -func (t *transaction) Rollback() error { - t.Lock() - defer t.Unlock() - return t.tx.Rollback() -} - -type executor struct { - sync.RWMutex - db db.Sqler -} - -func (x *executor) GetSegmentRsvFromID(ctx context.Context, ID *reservation.SegmentID) ( - *segment.Reservation, error) { - - params := []interface{}{ - ID.ASID, - binary.BigEndian.Uint32(ID.Suffix[:]), - } - rsvs, err := getSegReservations(ctx, x.db, "WHERE id_as = ? AND id_suffix = ?", params) - if err != nil { - return nil, err - } - switch len(rsvs) { - case 0: - return nil, nil - case 1: - return rsvs[0], nil - default: - return nil, db.NewDataError("more than 1 segment reservation found for an ID", nil, - "count", len(rsvs), "id.asid", ID.ASID, "id.suffix", hex.EncodeToString(ID.Suffix[:])) - } -} - -// GetSegmentRsvsFromSrcDstIA returns all reservations that start at src AS and end in dst AS. -func (x *executor) GetSegmentRsvsFromSrcDstIA(ctx context.Context, srcIA, dstIA addr.IA) ( - []*segment.Reservation, error) { - - conditions := make([]string, 0, 2) - params := make([]interface{}, 0, 2) - if !srcIA.IsZero() { - conditions = append(conditions, "src_ia = ?") - params = append(params, srcIA) - } - if !dstIA.IsZero() { - conditions = append(conditions, "dst_ia = ?") - params = append(params, dstIA) - } - if len(conditions) == 0 { - return nil, serrors.New("no src or dst ia provided") - } - condition := fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND ")) - return getSegReservations(ctx, x.db, condition, params) -} - -// GetSegmentRsvFromPath searches for a segment reservation with the specified path. -func (x *executor) GetSegmentRsvFromPath(ctx context.Context, - path segment.ReservationTransparentPath) (*segment.Reservation, error) { - - rsvs, err := getSegReservations(ctx, x.db, "WHERE path = ?", []interface{}{path.ToRaw()}) - if err != nil { - return nil, err - } - switch len(rsvs) { - case 0: - return nil, nil - case 1: - return rsvs[0], nil - default: - return nil, db.NewDataError("more than 1 segment reservation found for a path", nil, - "path", path.String()) - } -} - -// GetAllSegmentRsvs returns all segment reservations. -func (x *executor) GetAllSegmentRsvs(ctx context.Context) ([]*segment.Reservation, error) { - return getSegReservations(ctx, x.db, "", nil) -} - -// GetSegmentRsvsFromIFPair returns all segment reservations that enter this AS at -// the specified ingress and exit at that egress. -func (x *executor) GetSegmentRsvsFromIFPair(ctx context.Context, ingress, egress *uint16) ( - []*segment.Reservation, error) { - - conditions := make([]string, 0, 2) - params := make([]interface{}, 0, 2) - if ingress != nil { - conditions = append(conditions, "ingress = ?") - params = append(params, *ingress) - } - if egress != nil { - conditions = append(conditions, "egress = ?") - params = append(params, *egress) - } - if len(conditions) == 0 { - return nil, serrors.New("no ingress or egress provided") - } - condition := fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND ")) - return getSegReservations(ctx, x.db, condition, params) -} - -// NewSegmentRsv creates a new segment reservation in the DB, with an unused reservation ID. -// The reservation must contain at least one index. -// The created ID is set in the reservation pointer argument. -func (x *executor) NewSegmentRsv(ctx context.Context, rsv *segment.Reservation) error { - var err error - for retries := 0; retries < 3; retries++ { - err = db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - suffix, err := newSuffix(ctx, tx, rsv.ID.ASID) - if err != nil { - return err - } - if err := insertNewSegReservation(ctx, tx, rsv, suffix); err != nil { - return err - } - binary.BigEndian.PutUint32(rsv.ID.Suffix[:], suffix) - return nil - }) - if err == nil { - return nil - } - sqliteError, ok := err.(sqlite3.Error) - if !ok || sqliteError.Code != sqlite3.ErrConstraint { - return db.NewTxError("error inserting segment reservation", err) - } - } - return db.NewTxError("error inserting segment reservation after 3 retries", err) -} - -func (x *executor) PersistSegmentRsv(ctx context.Context, rsv *segment.Reservation) error { - err := db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - err := deleteSegmentRsv(ctx, tx, &rsv.ID) - if err != nil { - return err - } - suffix := binary.BigEndian.Uint32(rsv.ID.Suffix[:]) - return insertNewSegReservation(ctx, tx, rsv, suffix) - }) - if err != nil { - return db.NewTxError("error persisting reservation", err) - } - return nil -} - -// DeleteExpiredIndices will remove expired indices from the DB. If a reservation is left -// without any index after removing the expired ones, it will also be removed. This applies to -// both segment and e2e reservations. -func (x *executor) DeleteExpiredIndices(ctx context.Context, now time.Time) (int, error) { - deletedIndices := 0 - err := db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - // delete e2e indices - rowIDs, rsvRowIDs, err := getExpiredE2EIndexRowIDs(ctx, tx, now) - if err != nil { - return err - } - if len(rowIDs) > 0 { - // delete the segment indices pointed by rowIDs - n, err := deleteE2EIndicesFromRowIDs(ctx, tx, rowIDs) - if err != nil { - return err - } - deletedIndices = n - // delete empty reservations touched by previous removal - err = deleteEmptyE2EReservations(ctx, tx, rsvRowIDs) - if err != nil { - return err - } - } - - // delete segment indices - rowIDs, rsvRowIDs, err = getExpiredSegIndexRowIDs(ctx, tx, now) - if err != nil { - return err - } - if len(rowIDs) > 0 { - // delete the segment indices pointed by rowIDs - n, err := deleteSegIndicesFromRowIDs(ctx, tx, rowIDs) - if err != nil { - return err - } - deletedIndices += n - // delete empty reservations touched by previous removal - return deleteEmptySegReservations(ctx, tx, rsvRowIDs) - } - return nil - }) - return deletedIndices, err -} - -// DeleteSegmentRsv removes the segment reservation -func (x *executor) DeleteSegmentRsv(ctx context.Context, ID *reservation.SegmentID) error { - return deleteSegmentRsv(ctx, x.db, ID) -} - -// GetE2ERsvFromID finds the end to end resevation given its ID. -func (x *executor) GetE2ERsvFromID(ctx context.Context, ID *reservation.E2EID) ( - *e2e.Reservation, error) { - - var rsv *e2e.Reservation - err := db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - var err error - rsv, err = getE2ERsvFromID(ctx, tx, ID) - return err - }) - return rsv, err -} - -// GetE2ERsvsOnSegRsv returns the e2e reservations running on top of a given segment one. -func (x *executor) GetE2ERsvsOnSegRsv(ctx context.Context, ID *reservation.SegmentID) ( - []*e2e.Reservation, error) { - - var rsvs []*e2e.Reservation - err := db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - var err error - rsvs, err = getE2ERsvsFromSegment(ctx, tx, ID) - return err - }) - return rsvs, err -} - -func (x *executor) PersistE2ERsv(ctx context.Context, rsv *e2e.Reservation) error { - err := db.DoInTx(ctx, x.db, func(ctx context.Context, tx *sql.Tx) error { - err := deleteE2ERsv(ctx, tx, &rsv.ID) - if err != nil { - return err - } - return insertNewE2EReservation(ctx, tx, rsv) - }) - if err != nil { - return db.NewTxError("error persisting e2e reservation", err) - } - return nil -} - -// newSuffix finds a segment reservation ID suffix not being used at the moment. Should be called -// inside a transaction so the suffix is not used in the meantime, or fail. -func newSuffix(ctx context.Context, x db.Sqler, ASID addr.AS) (uint32, error) { - const query = `SELECT MIN(id_suffix)+1 FROM ( - SELECT 0 AS id_suffix UNION ALL - SELECT id_suffix FROM seg_reservation WHERE id_as = $1 - ) WHERE id_suffix+1 NOT IN (SELECT id_suffix FROM seg_reservation WHERE id_as = $1)` - var suffix uint32 - err := x.QueryRowContext(ctx, query, uint64(ASID)).Scan(&suffix) - switch { - case err == sql.ErrNoRows: - return 0, serrors.New("unexpected error getting new suffix: no rows") - case err != nil: - return 0, serrors.WrapStr("unexpected error getting new suffix", err) - } - return suffix, nil -} - -func insertNewSegReservation(ctx context.Context, x *sql.Tx, rsv *segment.Reservation, - suffix uint32) error { - - activeIndex := -1 - if rsv.ActiveIndex() != nil { - activeIndex = int(rsv.ActiveIndex().Idx) - } - const query = `INSERT INTO seg_reservation (id_as, id_suffix, ingress, egress, - path, end_props, traffic_split, src_ia, dst_ia,active_index) - VALUES (?, ?,?,?,?,?,?,?,?,?)` - res, err := x.ExecContext(ctx, query, rsv.ID.ASID, suffix, - rsv.Ingress, rsv.Egress, rsv.Path.ToRaw(), rsv.PathEndProps, rsv.TrafficSplit, - rsv.Path.GetSrcIA(), rsv.Path.GetDstIA(), activeIndex) - if err != nil { - return err - } - if len(rsv.Indices) > 0 { - rsvRowID, err := res.LastInsertId() - if err != nil { - return db.NewTxError("cannot obtain last insertion row id", err) - } - const queryIndexTmpl = `INSERT INTO seg_index (reservation, index_number, expiration, state, - min_bw, max_bw, alloc_bw, token) VALUES (?,?,?,?,?,?,?,?)` - params := make([]interface{}, 0, 8*len(rsv.Indices)) - for _, index := range rsv.Indices { - params = append(params, rsvRowID, index.Idx, - util.TimeToSecs(index.Expiration), index.State(), index.MinBW, index.MaxBW, - index.AllocBW, index.Token.ToRaw()) - } - q := queryIndexTmpl + strings.Repeat(",(?,?,?,?,?,?,?,?)", len(rsv.Indices)-1) - _, err = x.ExecContext(ctx, q, params...) - if err != nil { - return err - } - } - return nil -} - -type rsvFields struct { - RowID int - AsID uint64 - Suffix uint32 - Ingress uint16 - Egress uint16 - Path []byte - EndProps int - TrafficSplit int - ActiveIndex int -} - -func getSegReservations(ctx context.Context, x db.Sqler, condition string, params []interface{}) ( - []*segment.Reservation, error) { - - const queryTmpl = `SELECT ROWID,id_as,id_suffix,ingress,egress,path, - end_props,traffic_split,active_index - FROM seg_reservation %s` - query := fmt.Sprintf(queryTmpl, condition) - - rows, err := x.QueryContext(ctx, query, params...) - if err != nil { - return nil, err - } - defer rows.Close() - - reservationFields := []*rsvFields{} - for rows.Next() { - var f rsvFields - err := rows.Scan(&f.RowID, &f.AsID, &f.Suffix, &f.Ingress, &f.Egress, &f.Path, - &f.EndProps, &f.TrafficSplit, &f.ActiveIndex) - if err != nil { - return nil, err - } - reservationFields = append(reservationFields, &f) - } - if err := rows.Err(); err != nil { - return nil, err - } - - reservations := []*segment.Reservation{} - for _, rf := range reservationFields { - rsv, err := buildSegRsvFromFields(ctx, x, rf) - if err != nil { - return nil, err - } - reservations = append(reservations, rsv) - } - return reservations, nil -} - -// builds a segment.Reservation in memory from the fields and indices. -func buildSegRsvFromFields(ctx context.Context, x db.Sqler, fields *rsvFields) ( - *segment.Reservation, error) { - - indices, err := getSegIndices(ctx, x, fields.RowID) - if err != nil { - return nil, err - } - rsv := segment.NewReservation() - rsv.ID.ASID = addr.AS(fields.AsID) - binary.BigEndian.PutUint32(rsv.ID.Suffix[:], fields.Suffix) - rsv.Ingress = fields.Ingress - rsv.Egress = fields.Egress - p, err := segment.NewPathFromRaw(fields.Path) - if err != nil { - return nil, err - } - rsv.Path = p - rsv.PathEndProps = reservation.PathEndProps(fields.EndProps) - rsv.TrafficSplit = reservation.SplitCls(fields.TrafficSplit) - rsv.Indices = indices - if fields.ActiveIndex != -1 { - if err := rsv.SetIndexActive(reservation.IndexNumber(fields.ActiveIndex)); err != nil { - return nil, err - } - } - return rsv, nil -} - -// the rowID argument is the reservation row ID the indices belong to. -func getSegIndices(ctx context.Context, x db.Sqler, rowID int) (segment.Indices, error) { - const query = `SELECT index_number,expiration,state,min_bw,max_bw,alloc_bw,token - FROM seg_index WHERE reservation=?` - rows, err := x.QueryContext(ctx, query, rowID) - if err != nil { - return nil, db.NewReadError("cannot list indices", err) - } - defer rows.Close() - - indices := segment.Indices{} - var idx, expiration, state, minBW, maxBW, allocBW uint32 - var token []byte - for rows.Next() { - err := rows.Scan(&idx, &expiration, &state, &minBW, &maxBW, &allocBW, &token) - if err != nil { - return nil, db.NewReadError("could not get index values", err) - } - tok, err := reservation.TokenFromRaw(token) - if err != nil { - return nil, db.NewReadError("invalid stored token", err) - } - index := segment.NewIndex(reservation.IndexNumber(idx), - util.SecsToTime(expiration), segment.IndexState(state), reservation.BWCls(minBW), - reservation.BWCls(maxBW), reservation.BWCls(allocBW), tok) - indices = append(indices, *index) - } - if err := rows.Err(); err != nil { - return nil, err - } - // sort indices so they are consecutive modulo 16 - base.SortIndices(indices) - return indices, nil -} - -func deleteSegmentRsv(ctx context.Context, x db.Sqler, rsvID *reservation.SegmentID) error { - const query = `DELETE FROM seg_reservation WHERE id_as = ? AND id_suffix = ?` - suffix := binary.BigEndian.Uint32(rsvID.Suffix[:]) - _, err := x.ExecContext(ctx, query, rsvID.ASID, suffix) - return err -} - -func deleteE2ERsv(ctx context.Context, x db.Sqler, rsvID *reservation.E2EID) error { - const query = `DELETE FROM e2e_reservation WHERE reservation_id = ?` - _, err := x.ExecContext(ctx, query, rsvID.ToRaw()) - return err -} - -func insertNewE2EReservation(ctx context.Context, x *sql.Tx, rsv *e2e.Reservation) error { - const query = `INSERT INTO e2e_reservation (reservation_id) VALUES (?)` - res, err := x.ExecContext(ctx, query, rsv.ID.ToRaw()) - if err != nil { - return err - } - rowID, err := res.LastInsertId() - if err != nil { - return err - } - if len(rsv.Indices) > 0 { - const queryTmpl = `INSERT INTO e2e_index (reservation, index_number, expiration, - alloc_bw, token) VALUES (?,?,?,?,?)` - params := make([]interface{}, 0, 5*len(rsv.Indices)) - for _, index := range rsv.Indices { - params = append(params, rowID, index.Idx, util.TimeToSecs(index.Expiration), - index.AllocBW, index.Token.ToRaw()) - } - query := queryTmpl + strings.Repeat(",(?,?,?,?,?)", len(rsv.Indices)-1) - _, err := x.ExecContext(ctx, query, params...) - if err != nil { - return err - } - } - if len(rsv.SegmentReservations) > 0 { - const valuesPlaceholder = `(id_as = ? AND id_suffix = ?)` - const queryTmpl = `INSERT INTO e2e_to_seg (e2e, seg) - SELECT ?, ROWID FROM seg_reservation WHERE ` - params := make([]interface{}, 1, 1+2*len(rsv.SegmentReservations)) - params[0] = rowID - for _, segRsv := range rsv.SegmentReservations { - params = append(params, segRsv.ID.ASID, binary.BigEndian.Uint32(segRsv.ID.Suffix[:])) - } - query := queryTmpl + valuesPlaceholder + - strings.Repeat(" OR "+valuesPlaceholder, len(rsv.SegmentReservations)-1) - res, err := x.ExecContext(ctx, query, params...) - if err != nil { - return err - } - n, err := res.RowsAffected() - if err != nil { - return err - } - if int(n) != len(rsv.SegmentReservations) { - return serrors.New("not all referenced segment reservations are in DB", - "expected", len(rsv.SegmentReservations), "actual", n) - } - } - return nil -} - -func getE2ERsvFromID(ctx context.Context, x *sql.Tx, ID *reservation.E2EID) ( - *e2e.Reservation, error) { - - // read reservation - var rowID int - const query = `SELECT ROWID FROM e2e_reservation WHERE reservation_id = ?` - err := x.QueryRowContext(ctx, query, ID.ToRaw()).Scan(&rowID) - if err != nil { - if err == sql.ErrNoRows { - return nil, nil - } - return nil, err - } - // read indices - indices, err := getE2EIndices(ctx, x, rowID) - if err != nil { - return nil, err - } - // sort indices so they are consecutive modulo 16 - base.SortIndices(indices) - // read assoc segment reservations - segRsvs, err := getE2EAssocSegRsvs(ctx, x, rowID) - if err != nil { - return nil, err - } - rsv := &e2e.Reservation{ - ID: *ID, - Indices: indices, - SegmentReservations: segRsvs, - } - return rsv, nil -} - -func getE2ERsvsFromSegment(ctx context.Context, x *sql.Tx, ID *reservation.SegmentID) ( - []*e2e.Reservation, error) { - - rowID2e2eIDs := make(map[int]*reservation.E2EID) - const query = `SELECT ROWID,reservation_id FROM e2e_reservation WHERE ROWID IN ( - SELECT e2e FROM e2e_to_seg WHERE seg = ( - SELECT ROWID FROM seg_reservation WHERE id_as = ? AND id_suffix = ? - ))` - suffix := binary.BigEndian.Uint32(ID.Suffix[:]) - rows, err := x.QueryContext(ctx, query, ID.ASID, suffix) - if err != nil { - return nil, err - } - defer rows.Close() - for rows.Next() { - var rowID int - var rsvID []byte - err := rows.Scan(&rowID, &rsvID) - if err != nil { - return nil, err - } - id, err := reservation.E2EIDFromRaw(rsvID) - if err != nil { - return nil, err - } - rowID2e2eIDs[rowID] = id - } - if err := rows.Err(); err != nil { - return nil, err - } - rsvs := make([]*e2e.Reservation, 0, len(rowID2e2eIDs)) - for rowID, e2eID := range rowID2e2eIDs { - // read indices - indices, err := getE2EIndices(ctx, x, rowID) - if err != nil { - return nil, err - } - // read assoc segment reservations - segRsvs, err := getE2EAssocSegRsvs(ctx, x, rowID) - if err != nil { - return nil, err - } - rsv := &e2e.Reservation{ - ID: *e2eID, - Indices: indices, - SegmentReservations: segRsvs, - } - rsvs = append(rsvs, rsv) - } - return rsvs, nil -} - -func getE2EIndices(ctx context.Context, x db.Sqler, rowID int) (e2e.Indices, error) { - const query = `SELECT index_number, expiration, alloc_bw, token FROM e2e_index - WHERE reservation = ?` - rows, err := x.QueryContext(ctx, query, rowID) - if err != nil { - return nil, err - } - defer rows.Close() - indices := e2e.Indices{} - for rows.Next() { - var idx, expiration, allocBW uint32 - var token []byte - err := rows.Scan(&idx, &expiration, &allocBW, &token) - if err != nil { - return nil, err - } - tok, err := reservation.TokenFromRaw(token) - if err != nil { - return nil, err - } - indices = append(indices, e2e.Index{ - Idx: reservation.IndexNumber(idx), - Expiration: util.SecsToTime(expiration), - AllocBW: reservation.BWCls(allocBW), - Token: tok, - }) - } - if err := rows.Err(); err != nil { - return nil, err - } - return indices, nil -} - -// rowID is the row ID of the E2E reservation -func getE2EAssocSegRsvs(ctx context.Context, x db.Sqler, rowID int) ( - []*segment.Reservation, error) { - - condition := "WHERE rowID IN (SELECT seg FROM e2e_to_seg WHERE e2e = ?)" - return getSegReservations(ctx, x, condition, []interface{}{rowID}) -} - -// returns the rowIDs of the indices and their associated segment reservation rowID -func getExpiredSegIndexRowIDs(ctx context.Context, x db.Sqler, now time.Time) ( - []interface{}, []interface{}, error) { - - const query = `SELECT rowID, reservation FROM seg_index WHERE expiration < ?` - expTime := util.TimeToSecs(now) - rows, err := x.QueryContext(ctx, query, expTime) - if err != nil { - return nil, nil, err - } - defer rows.Close() - rowIDs := make([]interface{}, 0) - rsvRowIDs := make([]interface{}, 0) - for rows.Next() { - var indexID, rsvRowID int - err := rows.Scan(&indexID, &rsvRowID) - if err != nil { - return nil, nil, err - } - rowIDs = append(rowIDs, indexID) - rsvRowIDs = append(rsvRowIDs, rsvRowID) - } - if err := rows.Err(); err != nil { - return nil, nil, err - } - return rowIDs, rsvRowIDs, nil -} - -func deleteSegIndicesFromRowIDs(ctx context.Context, x db.Sqler, rowIDs []interface{}) ( - int, error) { - - const queryTmpl = `DELETE FROM seg_index WHERE ROWID IN (?%s)` - query := fmt.Sprintf(queryTmpl, strings.Repeat(",?", len(rowIDs)-1)) - res, err := x.ExecContext(ctx, query, rowIDs...) - if err != nil { - return 0, err - } - n, err := res.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -// deletes segment reservations from the rowIDs if they have no indices -func deleteEmptySegReservations(ctx context.Context, x db.Sqler, rowIDs []interface{}) error { - const queryTmpl = `DELETE FROM seg_reservation AS r WHERE NOT EXISTS ( - SELECT NULL FROM seg_index AS idx WHERE idx.reservation=r.ROWID - ) AND r.ROWID IN (?%s);` - query := fmt.Sprintf(queryTmpl, strings.Repeat(",?", len(rowIDs)-1)) - _, err := x.ExecContext(ctx, query, rowIDs...) - return err -} - -func getExpiredE2EIndexRowIDs(ctx context.Context, x db.Sqler, now time.Time) ( - []interface{}, []interface{}, error) { - - const query = `SELECT ROWID, reservation FROM e2e_index WHERE expiration < ?` - expTime := util.TimeToSecs(now) - rows, err := x.QueryContext(ctx, query, expTime) - if err != nil { - return nil, nil, err - } - defer rows.Close() - rowIDs := make([]interface{}, 0) - rsvRowIDs := make([]interface{}, 0) - for rows.Next() { - var indexID, rsvRowID int - err := rows.Scan(&indexID, &rsvRowID) - if err != nil { - return nil, nil, err - } - rowIDs = append(rowIDs, indexID) - rsvRowIDs = append(rsvRowIDs, rsvRowID) - } - if err := rows.Err(); err != nil { - return nil, nil, err - } - return rowIDs, rsvRowIDs, nil -} - -func deleteE2EIndicesFromRowIDs(ctx context.Context, x db.Sqler, rowIDs []interface{}) ( - int, error) { - - const queryTmpl = `DELETE FROM e2e_index WHERE ROWID IN (?%s)` - query := fmt.Sprintf(queryTmpl, strings.Repeat(",?", len(rowIDs)-1)) - res, err := x.ExecContext(ctx, query, rowIDs...) - if err != nil { - return 0, err - } - n, err := res.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -// deletes e2e reservations from the rowIDs if they have no indices -func deleteEmptyE2EReservations(ctx context.Context, x db.Sqler, rowIDs []interface{}) error { - const queryTmpl = `DELETE FROM e2e_reservation AS r WHERE NOT EXISTS ( - SELECT NULL FROM e2e_index AS idx WHERE idx.reservation=r.ROWID - ) AND r.ROWID IN (?%s);` - query := fmt.Sprintf(queryTmpl, strings.Repeat(",?", len(rowIDs)-1)) - _, err := x.ExecContext(ctx, query, rowIDs...) - return err -} diff --git a/control/colibri/reservation/sqlite/db_test.go b/control/colibri/reservation/sqlite/db_test.go deleted file mode 100644 index b9c4edead9..0000000000 --- a/control/colibri/reservation/sqlite/db_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 sqlite - -import ( - "context" - "database/sql" - "testing" - - "github.com/mattn/go-sqlite3" - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/control/colibri/reservation/reservationdbtest" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/xtest" -) - -type TestDB struct { - *Backend -} - -func (b *TestDB) Prepare(t *testing.T, _ context.Context) { - b.Backend = newDB(t) -} - -func TestReservationDBSuite(t *testing.T) { - db := &TestDB{} - reservationdbtest.TestDB(t, db) -} - -func TestNewSuffix(t *testing.T) { - ctx := context.Background() - asid := xtest.MustParseAS("ff00:0:1") - db := newDB(t) - suffix, err := newSuffix(ctx, db.db, asid) - require.NoError(t, err) - require.Equal(t, uint32(1), suffix) - // add reservations - addSegRsvRows(t, db, asid, 3, 5) - suffix, err = newSuffix(ctx, db.db, asid) - require.NoError(t, err) - require.Equal(t, uint32(1), suffix) - addSegRsvRows(t, db, asid, 1, 2) - suffix, err = newSuffix(ctx, db.db, asid) - require.NoError(t, err) - require.Equal(t, uint32(6), suffix) -} - -func TestRaceForSuffix(t *testing.T) { - ctx := context.Background() - asid := xtest.MustParseAS("ff00:0:1") - db := newDB(t) - addSegRsvRows(t, db, asid, 1, 2) - suffix1, err := newSuffix(ctx, db.db, asid) - require.NoError(t, err) - require.Equal(t, uint32(3), suffix1) - suffix2, err := newSuffix(ctx, db.db, asid) - require.NoError(t, err) - require.Equal(t, uint32(3), suffix2) - rsv := &segment.Reservation{ - ID: reservation.SegmentID{ASID: asid}, - Indices: segment.Indices{segment.Index{}}, - } - err = testInsertNewSegReservation(ctx, t, db.db, rsv, suffix1) - require.NoError(t, err) - err = testInsertNewSegReservation(ctx, t, db.db, rsv, suffix2) - require.Error(t, err) - sqliteError, ok := err.(sqlite3.Error) - require.True(t, ok) - require.Equal(t, sqlite3.ErrConstraint, sqliteError.Code) -} - -func BenchmarkNewSuffix10K(b *testing.B) { benchmarkNewSuffix(b, 10000) } -func BenchmarkNewSuffix100K(b *testing.B) { benchmarkNewSuffix(b, 100000) } -func BenchmarkNewSuffix1M(b *testing.B) { benchmarkNewSuffix(b, 1000000) } - -func newDB(t testing.TB) *Backend { - t.Helper() - db, err := New("file::memory:") - require.NoError(t, err) - return db -} - -func addSegRsvRows(t testing.TB, b *Backend, asid addr.AS, firstSuffix, lastSuffix uint32) { - t.Helper() - ctx := context.Background() - query := `INSERT INTO seg_reservation (id_as, id_suffix, ingress, egress, path, - end_props, traffic_split, src_ia, dst_ia, active_index) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, -1)` - for suffix := firstSuffix; suffix <= lastSuffix; suffix++ { - _, err := b.db.ExecContext(ctx, query, asid, suffix, 0, 0, nil, 0, 0, nil, nil) - require.NoError(t, err) - } -} - -func testInsertNewSegReservation(ctx context.Context, t *testing.T, db *sql.DB, - rsv *segment.Reservation, suffix uint32) error { - - tx, err := db.BeginTx(ctx, nil) - require.NoError(t, err) - defer tx.Rollback() - err = insertNewSegReservation(ctx, tx, rsv, suffix) - if err != nil { - return err - } - return tx.Commit() -} - -func benchmarkNewSuffix(b *testing.B, entries uint32) { - db := newDB(b) - ctx := context.Background() - asid := xtest.MustParseAS("ff00:0:1") - addSegRsvRows(b, db, asid, 1, entries) - b.ResetTimer() - - for n := 0; n < b.N; n++ { - suffix, err := newSuffix(ctx, db.db, asid) - require.NoError(b, err) - require.Equal(b, entries+1, suffix) - } -} diff --git a/control/colibri/reservation/sqlite/schema.go b/control/colibri/reservation/sqlite/schema.go deleted file mode 100644 index 6762c69ff3..0000000000 --- a/control/colibri/reservation/sqlite/schema.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 sqlite - -const ( - // SchemaVersion is the version of the SQLite schema understood by this backend. - // Whenever changes to the schema are made, this version number should be increased - // to prevent data corruption between incompatible database schemas. - SchemaVersion = 1 - // Schema is the SQLite database layout. - Schema = `CREATE TABLE seg_reservation ( - ROWID INTEGER, - id_as INTEGER NOT NULL, - id_suffix INTEGER NOT NULL, - ingress INTEGER NOT NULL, - egress INTEGER NOT NULL, - path BLOB, - end_props INTEGER NOT NULL, - traffic_split INTEGER NOT NULL, - src_ia INTEGER, - dst_ia INTEGER, - active_index INTEGER NOT NULL, - PRIMARY KEY(ROWID), - UNIQUE(id_as,id_suffix), - UNIQUE(path) - ); - CREATE TABLE seg_index ( - reservation INTEGER NOT NULL, - index_number INTEGER NOT NULL, - expiration INTEGER NOT NULL, - state INTEGER NOT NULL, - min_bw INTEGER NOT NULL, - max_bw INTEGER NOT NULL, - alloc_bw INTEGER NOT NULL, - token BLOB, - PRIMARY KEY(reservation,index_number), - FOREIGN KEY(reservation) REFERENCES seg_reservation(ROWID) ON DELETE CASCADE - ); - CREATE TABLE e2e_reservation ( - ROWID INTEGER, - reservation_id BLOB NOT NULL, - UNIQUE(reservation_id), - PRIMARY KEY(ROWID) - ); - CREATE TABLE e2e_index ( - reservation INTEGER NOT NULL, - index_number INTEGER NOT NULL, - expiration INTEGER NOT NULL, - alloc_bw INTEGER NOT NULL, - token BLOB, - PRIMARY KEY(reservation,index_number), - FOREIGN KEY(reservation) REFERENCES e2e_reservation(ROWID) ON DELETE CASCADE - ); - CREATE TABLE e2e_to_seg ( - e2e INTEGER NOT NULL, - seg INTEGER NOT NULL, - PRIMARY KEY(e2e,seg), - FOREIGN KEY(seg) REFERENCES seg_reservation(ROWID) ON DELETE CASCADE, - FOREIGN KEY(e2e) REFERENCES e2e_reservation(ROWID) ON DELETE CASCADE - ); - CREATE INDEX "index_seg_reservation" ON "seg_reservation" ( - "id_as", - "id_suffix" - ); - CREATE INDEX "index2_seg_reservation" ON "seg_reservation" ( - "ingress" - ); - CREATE INDEX "index3_seg_reservation" ON "seg_reservation" ( - "egress" - ); - CREATE UNIQUE INDEX "index4_seg_reservation" ON "seg_reservation" ( - "path" - ); - CREATE UNIQUE INDEX "index_seg_index" ON "seg_index" ( - "reservation", - "index_number" - ); - CREATE UNIQUE INDEX "index_e2e_reservation" ON "e2e_reservation" ( - "reservation_id" - ); - CREATE UNIQUE INDEX "index_e2e_index" ON "e2e_index" ( - "reservation", - "index_number" - ); - CREATE INDEX "index_e2e_to_seg" ON "e2e_to_seg" ( - "e2e" - ); - CREATE INDEX "index2_e2e_to_seg" ON "e2e_to_seg" ( - "seg" - );` -) diff --git a/control/colibri/reservation/test/BUILD.bazel b/control/colibri/reservation/test/BUILD.bazel deleted file mode 100644 index 74bb10dd42..0000000000 --- a/control/colibri/reservation/test/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["path.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservation/test", - visibility = ["//visibility:public"], - deps = ["//control/colibri/reservation:go_default_library"], -) diff --git a/control/colibri/reservation/test/path.go b/control/colibri/reservation/test/path.go deleted file mode 100644 index ecf81a2db8..0000000000 --- a/control/colibri/reservation/test/path.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 test - -import ( - base "github.com/scionproto/scion/control/colibri/reservation" -) - -type TestColibriPath struct { - HopCount int - CurrentHop int - Ingress uint16 - Egress uint16 -} - -var _ base.ColibriPath = (*TestColibriPath)(nil) - -func (p *TestColibriPath) Copy() base.ColibriPath { - return p -} - -func (p *TestColibriPath) Reverse() error { - return nil -} - -func (p *TestColibriPath) NumberOfHops() int { - return p.HopCount -} - -func (p *TestColibriPath) IndexOfCurrentHop() int { - return p.CurrentHop -} - -func (p *TestColibriPath) IngressEgressIFIDs() (uint16, uint16) { - return p.Ingress, p.Egress -} - -// NewTestPath returns a new path with one segment consisting on 3 hopfields: (0,2)->(1,2)->(1,0). -func NewTestPath() base.ColibriPath { - path := TestColibriPath{ - Ingress: 1, - Egress: 2, - } - return &path -} diff --git a/control/colibri/reservation/types.go b/control/colibri/reservation/types.go deleted file mode 100644 index 300ed5765d..0000000000 --- a/control/colibri/reservation/types.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservation - -// Capacities describes what a capacity description must offer. -type Capacities interface { - IngressInterfaces() []uint16 - EgressInterfaces() []uint16 - Capacity(from, to uint16) uint64 - CapacityIngress(ingress uint16) uint64 - CapacityEgress(egress uint16) uint64 -} - -// ColibriPath is a path of type COLIBRI. -// This type will be moved to its right place in slayers once the header has been approved. -// TODO(juagargi): move the type to slayers. -type ColibriPath interface { - Copy() ColibriPath - // Reverse reverses the contained path. - Reverse() error - NumberOfHops() int - IndexOfCurrentHop() int - IngressEgressIFIDs() (uint16, uint16) -} - -// MessageWithPath is used to send messages from the COLIBRI service via the BR. -type MessageWithPath interface { - Path() ColibriPath - // Payload() []byte -} diff --git a/control/colibri/reservationstorage/BUILD.bazel b/control/colibri/reservationstorage/BUILD.bazel deleted file mode 100644 index 1f749c5dc0..0000000000 --- a/control/colibri/reservationstorage/BUILD.bazel +++ /dev/null @@ -1,14 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["store.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservationstorage", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//private/storage/cleaner:go_default_library", - ], -) diff --git a/control/colibri/reservationstorage/backend/BUILD.bazel b/control/colibri/reservationstorage/backend/BUILD.bazel deleted file mode 100644 index c328c5ad98..0000000000 --- a/control/colibri/reservationstorage/backend/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["db.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservationstorage/backend", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//private/storage/db:go_default_library", - ], -) diff --git a/control/colibri/reservationstorage/backend/db.go b/control/colibri/reservationstorage/backend/db.go deleted file mode 100644 index a99a1da224..0000000000 --- a/control/colibri/reservationstorage/backend/db.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 backend - -import ( - "context" - "database/sql" - "io" - "time" - - "github.com/scionproto/scion/control/colibri/reservation/e2e" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/private/storage/db" -) - -// ReserverOnly has the methods available to the AS that starts the reservation. -type ReserverOnly interface { - // GetSegmentRsvsFromSrcDstIA returns all reservations that start at src AS and end in dst AS. - GetSegmentRsvsFromSrcDstIA(ctx context.Context, srcIA, dstIA addr.IA) ( - []*segment.Reservation, error) - // GetSegmentRsvFromPath searches for a segment reservation with the specified path. - GetSegmentRsvFromPath(ctx context.Context, path segment.ReservationTransparentPath) ( - *segment.Reservation, error) - - // NewSegmentRsv creates a new segment reservation in the DB, with an unused reservation ID. - // The created ID is set in the reservation pointer argument. Used by setup req. - NewSegmentRsv(ctx context.Context, rsv *segment.Reservation) error -} - -// TransitOnly represents an AS in-path of a reservation, not the one originating it. -type TransitOnly interface { - // GetAllSegmentRsvs returns all segment reservations. Used by setup req. - GetAllSegmentRsvs(ctx context.Context) ([]*segment.Reservation, error) - // GetSegmentRsvsFromIFPair returns all segment reservations that enter this AS at - // the specified ingress and exit at that egress. Used by setup req. - GetSegmentRsvsFromIFPair(ctx context.Context, ingress, egress *uint16) ( - []*segment.Reservation, error) -} - -// ReserverAndTransit contains the functionality for any AS that has a COLIBRI service. -type ReserverAndTransit interface { - // GetSegmentRsvFromID will return the reservation with that ID. - // Used by setup/renew req/resp. and any request. - GetSegmentRsvFromID(ctx context.Context, ID *reservation.SegmentID) ( - *segment.Reservation, error) - // PersistSegmentRsv ensures the DB contains the reservation as represented in rsv. - PersistSegmentRsv(ctx context.Context, rsv *segment.Reservation) error - // DeleteSegmentRsv removes the segment reservation. Used in teardown. - DeleteSegmentRsv(ctx context.Context, ID *reservation.SegmentID) error - - // DeleteExpiredIndices will remove expired indices from the DB. If a reservation is left - // without any index after removing the expired ones, it will also be removed. This applies to - // both segment and e2e reservations. - // Used on schedule. - DeleteExpiredIndices(ctx context.Context, now time.Time) (int, error) - - // GetE2ERsvFromID finds the end to end resevation given its ID. - GetE2ERsvFromID(ctx context.Context, ID *reservation.E2EID) (*e2e.Reservation, error) - // GetE2ERsvsOnSegRsv returns the e2e reservations running on top of a given segment one. - GetE2ERsvsOnSegRsv(ctx context.Context, ID *reservation.SegmentID) ([]*e2e.Reservation, error) - // PersistE2ERsv makes the DB reflect the same contents as the rsv parameter. - PersistE2ERsv(ctx context.Context, rsv *e2e.Reservation) error -} - -type Transaction interface { - ReserverOnly - TransitOnly - ReserverAndTransit - Commit() error - Rollback() error -} - -// DB is the interface for any reservation backend. -type DB interface { - BeginTransaction(ctx context.Context, opts *sql.TxOptions) (Transaction, error) - ReserverOnly - TransitOnly - ReserverAndTransit - db.LimitSetter - io.Closer -} diff --git a/control/colibri/reservationstorage/backend/mock_backend/BUILD.bazel b/control/colibri/reservationstorage/backend/mock_backend/BUILD.bazel deleted file mode 100644 index f6c9ed1adb..0000000000 --- a/control/colibri/reservationstorage/backend/mock_backend/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("//tools/lint:go.bzl", "go_library") -load("@io_bazel_rules_go//go:def.bzl", "gomock") - -gomock( - name = "go_default_mock", - out = "mock.go", - interfaces = [ - "DB", - "Transaction", - ], - library = "//control/colibri/reservationstorage/backend:go_default_library", - package = "mock_backend", -) - -go_library( - name = "go_default_library", - srcs = ["mock.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservationstorage/backend/mock_backend", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservationstorage/backend:go_default_library", - "//pkg/addr:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "@com_github_golang_mock//gomock:go_default_library", - ], -) diff --git a/control/colibri/reservationstorage/backend/mock_backend/mock.go b/control/colibri/reservationstorage/backend/mock_backend/mock.go deleted file mode 100644 index fb8eb16294..0000000000 --- a/control/colibri/reservationstorage/backend/mock_backend/mock.go +++ /dev/null @@ -1,498 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/scionproto/scion/control/colibri/reservationstorage/backend (interfaces: DB,Transaction) - -// Package mock_backend is a generated GoMock package. -package mock_backend - -import ( - context "context" - sql "database/sql" - reflect "reflect" - time "time" - - gomock "github.com/golang/mock/gomock" - e2e "github.com/scionproto/scion/control/colibri/reservation/e2e" - segment "github.com/scionproto/scion/control/colibri/reservation/segment" - backend "github.com/scionproto/scion/control/colibri/reservationstorage/backend" - addr "github.com/scionproto/scion/pkg/addr" - reservation "github.com/scionproto/scion/pkg/experimental/colibri/reservation" -) - -// MockDB is a mock of DB interface. -type MockDB struct { - ctrl *gomock.Controller - recorder *MockDBMockRecorder -} - -// MockDBMockRecorder is the mock recorder for MockDB. -type MockDBMockRecorder struct { - mock *MockDB -} - -// NewMockDB creates a new mock instance. -func NewMockDB(ctrl *gomock.Controller) *MockDB { - mock := &MockDB{ctrl: ctrl} - mock.recorder = &MockDBMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDB) EXPECT() *MockDBMockRecorder { - return m.recorder -} - -// BeginTransaction mocks base method. -func (m *MockDB) BeginTransaction(arg0 context.Context, arg1 *sql.TxOptions) (backend.Transaction, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeginTransaction", arg0, arg1) - ret0, _ := ret[0].(backend.Transaction) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BeginTransaction indicates an expected call of BeginTransaction. -func (mr *MockDBMockRecorder) BeginTransaction(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTransaction", reflect.TypeOf((*MockDB)(nil).BeginTransaction), arg0, arg1) -} - -// Close mocks base method. -func (m *MockDB) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close. -func (mr *MockDBMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDB)(nil).Close)) -} - -// DeleteExpiredIndices mocks base method. -func (m *MockDB) DeleteExpiredIndices(arg0 context.Context, arg1 time.Time) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteExpiredIndices", arg0, arg1) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteExpiredIndices indicates an expected call of DeleteExpiredIndices. -func (mr *MockDBMockRecorder) DeleteExpiredIndices(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpiredIndices", reflect.TypeOf((*MockDB)(nil).DeleteExpiredIndices), arg0, arg1) -} - -// DeleteSegmentRsv mocks base method. -func (m *MockDB) DeleteSegmentRsv(arg0 context.Context, arg1 *reservation.SegmentID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteSegmentRsv indicates an expected call of DeleteSegmentRsv. -func (mr *MockDBMockRecorder) DeleteSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSegmentRsv", reflect.TypeOf((*MockDB)(nil).DeleteSegmentRsv), arg0, arg1) -} - -// GetAllSegmentRsvs mocks base method. -func (m *MockDB) GetAllSegmentRsvs(arg0 context.Context) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAllSegmentRsvs", arg0) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAllSegmentRsvs indicates an expected call of GetAllSegmentRsvs. -func (mr *MockDBMockRecorder) GetAllSegmentRsvs(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllSegmentRsvs", reflect.TypeOf((*MockDB)(nil).GetAllSegmentRsvs), arg0) -} - -// GetE2ERsvFromID mocks base method. -func (m *MockDB) GetE2ERsvFromID(arg0 context.Context, arg1 *reservation.E2EID) (*e2e.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetE2ERsvFromID", arg0, arg1) - ret0, _ := ret[0].(*e2e.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetE2ERsvFromID indicates an expected call of GetE2ERsvFromID. -func (mr *MockDBMockRecorder) GetE2ERsvFromID(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetE2ERsvFromID", reflect.TypeOf((*MockDB)(nil).GetE2ERsvFromID), arg0, arg1) -} - -// GetE2ERsvsOnSegRsv mocks base method. -func (m *MockDB) GetE2ERsvsOnSegRsv(arg0 context.Context, arg1 *reservation.SegmentID) ([]*e2e.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetE2ERsvsOnSegRsv", arg0, arg1) - ret0, _ := ret[0].([]*e2e.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetE2ERsvsOnSegRsv indicates an expected call of GetE2ERsvsOnSegRsv. -func (mr *MockDBMockRecorder) GetE2ERsvsOnSegRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetE2ERsvsOnSegRsv", reflect.TypeOf((*MockDB)(nil).GetE2ERsvsOnSegRsv), arg0, arg1) -} - -// GetSegmentRsvFromID mocks base method. -func (m *MockDB) GetSegmentRsvFromID(arg0 context.Context, arg1 *reservation.SegmentID) (*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvFromID", arg0, arg1) - ret0, _ := ret[0].(*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvFromID indicates an expected call of GetSegmentRsvFromID. -func (mr *MockDBMockRecorder) GetSegmentRsvFromID(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvFromID", reflect.TypeOf((*MockDB)(nil).GetSegmentRsvFromID), arg0, arg1) -} - -// GetSegmentRsvFromPath mocks base method. -func (m *MockDB) GetSegmentRsvFromPath(arg0 context.Context, arg1 segment.ReservationTransparentPath) (*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvFromPath", arg0, arg1) - ret0, _ := ret[0].(*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvFromPath indicates an expected call of GetSegmentRsvFromPath. -func (mr *MockDBMockRecorder) GetSegmentRsvFromPath(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvFromPath", reflect.TypeOf((*MockDB)(nil).GetSegmentRsvFromPath), arg0, arg1) -} - -// GetSegmentRsvsFromIFPair mocks base method. -func (m *MockDB) GetSegmentRsvsFromIFPair(arg0 context.Context, arg1, arg2 *uint16) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvsFromIFPair", arg0, arg1, arg2) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvsFromIFPair indicates an expected call of GetSegmentRsvsFromIFPair. -func (mr *MockDBMockRecorder) GetSegmentRsvsFromIFPair(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvsFromIFPair", reflect.TypeOf((*MockDB)(nil).GetSegmentRsvsFromIFPair), arg0, arg1, arg2) -} - -// GetSegmentRsvsFromSrcDstIA mocks base method. -func (m *MockDB) GetSegmentRsvsFromSrcDstIA(arg0 context.Context, arg1, arg2 addr.IA) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvsFromSrcDstIA", arg0, arg1, arg2) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvsFromSrcDstIA indicates an expected call of GetSegmentRsvsFromSrcDstIA. -func (mr *MockDBMockRecorder) GetSegmentRsvsFromSrcDstIA(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvsFromSrcDstIA", reflect.TypeOf((*MockDB)(nil).GetSegmentRsvsFromSrcDstIA), arg0, arg1, arg2) -} - -// NewSegmentRsv mocks base method. -func (m *MockDB) NewSegmentRsv(arg0 context.Context, arg1 *segment.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// NewSegmentRsv indicates an expected call of NewSegmentRsv. -func (mr *MockDBMockRecorder) NewSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSegmentRsv", reflect.TypeOf((*MockDB)(nil).NewSegmentRsv), arg0, arg1) -} - -// PersistE2ERsv mocks base method. -func (m *MockDB) PersistE2ERsv(arg0 context.Context, arg1 *e2e.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PersistE2ERsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// PersistE2ERsv indicates an expected call of PersistE2ERsv. -func (mr *MockDBMockRecorder) PersistE2ERsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistE2ERsv", reflect.TypeOf((*MockDB)(nil).PersistE2ERsv), arg0, arg1) -} - -// PersistSegmentRsv mocks base method. -func (m *MockDB) PersistSegmentRsv(arg0 context.Context, arg1 *segment.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PersistSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// PersistSegmentRsv indicates an expected call of PersistSegmentRsv. -func (mr *MockDBMockRecorder) PersistSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistSegmentRsv", reflect.TypeOf((*MockDB)(nil).PersistSegmentRsv), arg0, arg1) -} - -// SetMaxIdleConns mocks base method. -func (m *MockDB) SetMaxIdleConns(arg0 int) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetMaxIdleConns", arg0) -} - -// SetMaxIdleConns indicates an expected call of SetMaxIdleConns. -func (mr *MockDBMockRecorder) SetMaxIdleConns(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxIdleConns", reflect.TypeOf((*MockDB)(nil).SetMaxIdleConns), arg0) -} - -// SetMaxOpenConns mocks base method. -func (m *MockDB) SetMaxOpenConns(arg0 int) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetMaxOpenConns", arg0) -} - -// SetMaxOpenConns indicates an expected call of SetMaxOpenConns. -func (mr *MockDBMockRecorder) SetMaxOpenConns(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxOpenConns", reflect.TypeOf((*MockDB)(nil).SetMaxOpenConns), arg0) -} - -// MockTransaction is a mock of Transaction interface. -type MockTransaction struct { - ctrl *gomock.Controller - recorder *MockTransactionMockRecorder -} - -// MockTransactionMockRecorder is the mock recorder for MockTransaction. -type MockTransactionMockRecorder struct { - mock *MockTransaction -} - -// NewMockTransaction creates a new mock instance. -func NewMockTransaction(ctrl *gomock.Controller) *MockTransaction { - mock := &MockTransaction{ctrl: ctrl} - mock.recorder = &MockTransactionMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTransaction) EXPECT() *MockTransactionMockRecorder { - return m.recorder -} - -// Commit mocks base method. -func (m *MockTransaction) Commit() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Commit") - ret0, _ := ret[0].(error) - return ret0 -} - -// Commit indicates an expected call of Commit. -func (mr *MockTransactionMockRecorder) Commit() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockTransaction)(nil).Commit)) -} - -// DeleteExpiredIndices mocks base method. -func (m *MockTransaction) DeleteExpiredIndices(arg0 context.Context, arg1 time.Time) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteExpiredIndices", arg0, arg1) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteExpiredIndices indicates an expected call of DeleteExpiredIndices. -func (mr *MockTransactionMockRecorder) DeleteExpiredIndices(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpiredIndices", reflect.TypeOf((*MockTransaction)(nil).DeleteExpiredIndices), arg0, arg1) -} - -// DeleteSegmentRsv mocks base method. -func (m *MockTransaction) DeleteSegmentRsv(arg0 context.Context, arg1 *reservation.SegmentID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteSegmentRsv indicates an expected call of DeleteSegmentRsv. -func (mr *MockTransactionMockRecorder) DeleteSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSegmentRsv", reflect.TypeOf((*MockTransaction)(nil).DeleteSegmentRsv), arg0, arg1) -} - -// GetAllSegmentRsvs mocks base method. -func (m *MockTransaction) GetAllSegmentRsvs(arg0 context.Context) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAllSegmentRsvs", arg0) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAllSegmentRsvs indicates an expected call of GetAllSegmentRsvs. -func (mr *MockTransactionMockRecorder) GetAllSegmentRsvs(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllSegmentRsvs", reflect.TypeOf((*MockTransaction)(nil).GetAllSegmentRsvs), arg0) -} - -// GetE2ERsvFromID mocks base method. -func (m *MockTransaction) GetE2ERsvFromID(arg0 context.Context, arg1 *reservation.E2EID) (*e2e.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetE2ERsvFromID", arg0, arg1) - ret0, _ := ret[0].(*e2e.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetE2ERsvFromID indicates an expected call of GetE2ERsvFromID. -func (mr *MockTransactionMockRecorder) GetE2ERsvFromID(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetE2ERsvFromID", reflect.TypeOf((*MockTransaction)(nil).GetE2ERsvFromID), arg0, arg1) -} - -// GetE2ERsvsOnSegRsv mocks base method. -func (m *MockTransaction) GetE2ERsvsOnSegRsv(arg0 context.Context, arg1 *reservation.SegmentID) ([]*e2e.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetE2ERsvsOnSegRsv", arg0, arg1) - ret0, _ := ret[0].([]*e2e.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetE2ERsvsOnSegRsv indicates an expected call of GetE2ERsvsOnSegRsv. -func (mr *MockTransactionMockRecorder) GetE2ERsvsOnSegRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetE2ERsvsOnSegRsv", reflect.TypeOf((*MockTransaction)(nil).GetE2ERsvsOnSegRsv), arg0, arg1) -} - -// GetSegmentRsvFromID mocks base method. -func (m *MockTransaction) GetSegmentRsvFromID(arg0 context.Context, arg1 *reservation.SegmentID) (*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvFromID", arg0, arg1) - ret0, _ := ret[0].(*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvFromID indicates an expected call of GetSegmentRsvFromID. -func (mr *MockTransactionMockRecorder) GetSegmentRsvFromID(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvFromID", reflect.TypeOf((*MockTransaction)(nil).GetSegmentRsvFromID), arg0, arg1) -} - -// GetSegmentRsvFromPath mocks base method. -func (m *MockTransaction) GetSegmentRsvFromPath(arg0 context.Context, arg1 segment.ReservationTransparentPath) (*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvFromPath", arg0, arg1) - ret0, _ := ret[0].(*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvFromPath indicates an expected call of GetSegmentRsvFromPath. -func (mr *MockTransactionMockRecorder) GetSegmentRsvFromPath(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvFromPath", reflect.TypeOf((*MockTransaction)(nil).GetSegmentRsvFromPath), arg0, arg1) -} - -// GetSegmentRsvsFromIFPair mocks base method. -func (m *MockTransaction) GetSegmentRsvsFromIFPair(arg0 context.Context, arg1, arg2 *uint16) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvsFromIFPair", arg0, arg1, arg2) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvsFromIFPair indicates an expected call of GetSegmentRsvsFromIFPair. -func (mr *MockTransactionMockRecorder) GetSegmentRsvsFromIFPair(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvsFromIFPair", reflect.TypeOf((*MockTransaction)(nil).GetSegmentRsvsFromIFPair), arg0, arg1, arg2) -} - -// GetSegmentRsvsFromSrcDstIA mocks base method. -func (m *MockTransaction) GetSegmentRsvsFromSrcDstIA(arg0 context.Context, arg1, arg2 addr.IA) ([]*segment.Reservation, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSegmentRsvsFromSrcDstIA", arg0, arg1, arg2) - ret0, _ := ret[0].([]*segment.Reservation) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSegmentRsvsFromSrcDstIA indicates an expected call of GetSegmentRsvsFromSrcDstIA. -func (mr *MockTransactionMockRecorder) GetSegmentRsvsFromSrcDstIA(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegmentRsvsFromSrcDstIA", reflect.TypeOf((*MockTransaction)(nil).GetSegmentRsvsFromSrcDstIA), arg0, arg1, arg2) -} - -// NewSegmentRsv mocks base method. -func (m *MockTransaction) NewSegmentRsv(arg0 context.Context, arg1 *segment.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// NewSegmentRsv indicates an expected call of NewSegmentRsv. -func (mr *MockTransactionMockRecorder) NewSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSegmentRsv", reflect.TypeOf((*MockTransaction)(nil).NewSegmentRsv), arg0, arg1) -} - -// PersistE2ERsv mocks base method. -func (m *MockTransaction) PersistE2ERsv(arg0 context.Context, arg1 *e2e.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PersistE2ERsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// PersistE2ERsv indicates an expected call of PersistE2ERsv. -func (mr *MockTransactionMockRecorder) PersistE2ERsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistE2ERsv", reflect.TypeOf((*MockTransaction)(nil).PersistE2ERsv), arg0, arg1) -} - -// PersistSegmentRsv mocks base method. -func (m *MockTransaction) PersistSegmentRsv(arg0 context.Context, arg1 *segment.Reservation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PersistSegmentRsv", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// PersistSegmentRsv indicates an expected call of PersistSegmentRsv. -func (mr *MockTransactionMockRecorder) PersistSegmentRsv(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistSegmentRsv", reflect.TypeOf((*MockTransaction)(nil).PersistSegmentRsv), arg0, arg1) -} - -// Rollback mocks base method. -func (m *MockTransaction) Rollback() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Rollback") - ret0, _ := ret[0].(error) - return ret0 -} - -// Rollback indicates an expected call of Rollback. -func (mr *MockTransactionMockRecorder) Rollback() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockTransaction)(nil).Rollback)) -} diff --git a/control/colibri/reservationstorage/store.go b/control/colibri/reservationstorage/store.go deleted file mode 100644 index d8400a92ab..0000000000 --- a/control/colibri/reservationstorage/store.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservationstorage - -import ( - "context" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/e2e" - sgt "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/private/storage/cleaner" -) - -// Store is the interface to interact with the reservation store. -type Store interface { - AdmitSegmentReservation(ctx context.Context, req *sgt.SetupReq) ( - base.MessageWithPath, error) - ConfirmSegmentReservation(ctx context.Context, req *sgt.IndexConfirmationReq) ( - base.MessageWithPath, error) - CleanupSegmentReservation(ctx context.Context, req *sgt.CleanupReq) ( - base.MessageWithPath, error) - TearDownSegmentReservation(ctx context.Context, req *sgt.TeardownReq) ( - base.MessageWithPath, error) - AdmitE2EReservation(ctx context.Context, req e2e.SetupRequest) ( - base.MessageWithPath, error) - CleanupE2EReservation(ctx context.Context, req *e2e.CleanupReq) ( - base.MessageWithPath, error) - - DeleteExpiredIndices(ctx context.Context) (int, error) -} - -// TODO(juagargi) there is a number of functions missing: all regarding responses. - -// NewIndexCleaner creates a cleaner removing expired indices and reservations. -func NewIndexCleaner(s Store) *cleaner.Cleaner { - return cleaner.New(func(ctx context.Context) (int, error) { - return s.DeleteExpiredIndices(ctx) - }, "colibri") -} diff --git a/control/colibri/reservationstore/BUILD.bazel b/control/colibri/reservationstore/BUILD.bazel deleted file mode 100644 index 613b053c67..0000000000 --- a/control/colibri/reservationstore/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_test( - name = "go_default_test", - srcs = ["store_test.go"], - embed = [":go_default_library"], - deps = ["//control/colibri/reservationstorage:go_default_library"], -) - -go_library( - name = "go_default_library", - srcs = ["store.go"], - importpath = "github.com/scionproto/scion/control/colibri/reservationstore", - visibility = ["//visibility:public"], - deps = [ - "//control/colibri/reservation:go_default_library", - "//control/colibri/reservation/e2e:go_default_library", - "//control/colibri/reservation/segment:go_default_library", - "//control/colibri/reservation/segment/admission:go_default_library", - "//control/colibri/reservationstorage:go_default_library", - "//control/colibri/reservationstorage/backend:go_default_library", - "//pkg/experimental/colibri/reservation:go_default_library", - "//pkg/private/serrors:go_default_library", - ], -) diff --git a/control/colibri/reservationstore/store.go b/control/colibri/reservationstore/store.go deleted file mode 100644 index 9b6a5bb14b..0000000000 --- a/control/colibri/reservationstore/store.go +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservationstore - -import ( - "context" - "math" - "time" - - base "github.com/scionproto/scion/control/colibri/reservation" - "github.com/scionproto/scion/control/colibri/reservation/e2e" - "github.com/scionproto/scion/control/colibri/reservation/segment" - "github.com/scionproto/scion/control/colibri/reservation/segment/admission" - "github.com/scionproto/scion/control/colibri/reservationstorage" - "github.com/scionproto/scion/control/colibri/reservationstorage/backend" - "github.com/scionproto/scion/pkg/experimental/colibri/reservation" - "github.com/scionproto/scion/pkg/private/serrors" -) - -// Store is the reservation store. -type Store struct { - db backend.DB // aka reservation map - admitter admission.Admitter // the chosen admission entity -} - -var _ reservationstorage.Store = (*Store)(nil) - -// NewStore creates a new reservation store. -func NewStore(db backend.DB, admitter admission.Admitter) *Store { - return &Store{ - db: db, - admitter: admitter, - } -} - -// AdmitSegmentReservation receives a setup/renewal request to admit a segment reservation. -// It is expected that this AS is not the reservation initiator. -func (s *Store) AdmitSegmentReservation(ctx context.Context, req *segment.SetupReq) ( - base.MessageWithPath, error) { - - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating request", err, "id", req.ID) - } - if req.Path().IndexOfCurrentHop() != len(req.AllocTrail) { - return nil, serrors.New("inconsistent number of hops", - "len_alloctrail", len(req.AllocTrail), "hf_count", req.Path().IndexOfCurrentHop()) - } - - response, err := s.prepareFailureSegmentResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - failedResponse := &segment.ResponseSetupFailure{ - Response: *response, - FailedSetup: req, - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, - "id", req.ID) - } - defer tx.Rollback() - - rsv, err := tx.GetSegmentRsvFromID(ctx, &req.ID) - if err != nil { - return failedResponse, serrors.WrapStr("cannot obtain segment reservation", err, - "id", req.ID) - } - - if rsv != nil { - // renewal, ensure index is not used - index := rsv.Index(req.InfoField.Idx) - if index != nil { - return failedResponse, serrors.New("index from setup already in use", - "idx", req.InfoField.Idx, "id", req.ID) - } - } else { - // setup, create reservation and an index - rsv = segment.NewReservation() - rsv.ID = req.ID - err = tx.NewSegmentRsv(ctx, rsv) - if err != nil { - return failedResponse, serrors.WrapStr( - "unable to create a new segment reservation in db", err, - "id", req.ID) - } - } - req.Reservation = rsv - tok := &reservation.Token{InfoField: req.InfoField} - idx, err := rsv.NewIndexFromToken(tok, req.MinBW, req.MaxBW) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create index from token", err, - "id", req.ID) - } - index := rsv.Index(idx) - - // checkpath type compatibility with end properties - if err := rsv.PathEndProps.ValidateWithPathType(rsv.PathType); err != nil { - return failedResponse, serrors.WrapStr("error validating end props and path type", err, - "id", req.ID) - } - // compute admission max BW - // TODO(juagargi) use the transaction also in the admitter - err = s.admitter.AdmitRsv(ctx, req) - if err != nil { - return failedResponse, serrors.WrapStr("segment not admitted", err, "id", req.ID, - "index", req.Index) - } - // admitted; the request contains already the value inside the "allocation beads" of the rsv - index.AllocBW = req.AllocTrail[len(req.AllocTrail)-1].AllocBW - - if err = tx.PersistSegmentRsv(ctx, rsv); err != nil { - return failedResponse, serrors.WrapStr("cannot persist segment reservation", err, - "id", req.ID) - } - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, "id", req.ID) - } - - if req.IsLastAS() { - // TODO(juagargi) update token here - return &segment.ResponseSetupSuccess{ - Response: *morphSegmentResponseToSuccess(response), - Token: *index.Token, - }, nil - } - // TODO(juagargi) refactor function - return req, nil -} - -// ConfirmSegmentReservation changes the state of an index from temporary to confirmed. -func (s *Store) ConfirmSegmentReservation(ctx context.Context, req *segment.IndexConfirmationReq) ( - base.MessageWithPath, error) { - - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating request", err, "id", req.ID) - } - - response, err := s.prepareFailureSegmentResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - failedResponse := &segment.ResponseIndexConfirmationFailure{ - Response: *response, - ErrorCode: 1, // TODO(juagargi) specify error codes for every response - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, "id", req.ID) - } - defer tx.Rollback() - - rsv, err := tx.GetSegmentRsvFromID(ctx, &req.ID) - if err != nil { - return failedResponse, serrors.WrapStr("cannot obtain segment reservation", err, - "id", req.ID) - } - if err := rsv.SetIndexConfirmed(req.Index); err != nil { - return failedResponse, serrors.WrapStr("cannot set index to confirmed", err, - "id", req.ID) - } - if err = tx.PersistSegmentRsv(ctx, rsv); err != nil { - return failedResponse, serrors.WrapStr("cannot persist segment reservation", err, - "id", req.ID) - } - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, - "id", req.ID) - } - if req.IsLastAS() { - return &segment.ResponseIndexConfirmationSuccess{ - Response: *morphSegmentResponseToSuccess(response), - }, nil - } - return req, nil -} - -// CleanupSegmentReservation deletes an index from a segment reservation. -func (s *Store) CleanupSegmentReservation(ctx context.Context, req *segment.CleanupReq) ( - base.MessageWithPath, error) { - - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating request", err, "id", req.ID) - } - - response, err := s.prepareFailureSegmentResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - failedResponse := &segment.ResponseCleanupFailure{ - Response: *response, - ErrorCode: 1, - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, "id", req.ID) - } - defer tx.Rollback() - - rsv, err := tx.GetSegmentRsvFromID(ctx, &req.ID) - if err != nil { - return failedResponse, serrors.WrapStr("cannot obtain segment reservation", err, - "id", req.ID) - } - if err := rsv.RemoveIndex(req.Index); err != nil { - return failedResponse, serrors.WrapStr("cannot delete segment reservation index", err, - "id", req.ID, "index", req.Index) - } - if err = tx.PersistSegmentRsv(ctx, rsv); err != nil { - return failedResponse, serrors.WrapStr("cannot persist segment reservation", err, - "id", req.ID) - } - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, - "id", req.ID) - } - - if req.IsLastAS() { - return &segment.ResponseCleanupSuccess{ - Response: *morphSegmentResponseToSuccess(response), - }, nil - } - return req, nil -} - -// TearDownSegmentReservation removes a whole segment reservation. -func (s *Store) TearDownSegmentReservation(ctx context.Context, req *segment.TeardownReq) ( - base.MessageWithPath, error) { - - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating request", err, "id", req.ID) - } - - response, err := s.prepareFailureSegmentResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - failedResponse := &segment.ResponseTeardownFailure{ - Response: *response, - ErrorCode: 1, - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, "id", req.ID) - } - defer tx.Rollback() - - if err := tx.DeleteSegmentRsv(ctx, &req.ID); err != nil { - return failedResponse, serrors.WrapStr("cannot teardown reservation", err, - "id", req.ID) - } - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, - "id", req.ID) - } - - if req.IsLastAS() { - return &segment.ResponseTeardownSuccess{ - Response: *morphSegmentResponseToSuccess(response), - }, nil - } - return req, nil -} - -// AdmitE2EReservation will atempt to admit an e2e reservation. -func (s *Store) AdmitE2EReservation(ctx context.Context, request e2e.SetupRequest) ( - base.MessageWithPath, error) { - - req := request.GetCommonSetupReq() - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating e2e request", err, "id", req.ID) - } - - response, err := s.prepareFailureE2EResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - var failedResponse base.MessageWithPath - failedResponse = &e2e.ResponseSetupFailure{ - Response: *response, - ErrorCode: 1, - MaxBWs: req.AllocationTrail, - } - - // sanity check: all successful requests are SetupReqSuccess. Failed ones are SetupReqFailure. - if request.IsSuccessful() { - if _, ok := request.(*e2e.SetupReqSuccess); !ok { - return failedResponse, serrors.New("logic error, successful request can be casted") - } - } else { - if _, ok := request.(*e2e.SetupReqFailure); !ok { - return failedResponse, serrors.New("logic error, failed request can be casted") - } - } - - if len(req.SegmentRsvs) == 0 || len(req.SegmentRsvs) > 3 { - return failedResponse, serrors.New("invalid number of segment reservations for an e2e one", - "count", len(req.SegmentRsvs)) - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, - "id", req.ID) - } - defer tx.Rollback() - - rsv, err := tx.GetE2ERsvFromID(ctx, &req.ID) - if err != nil { - return failedResponse, serrors.WrapStr("cannot obtain e2e reservation", err, - "id", req.ID) - } - - segRsvIDs := req.SegmentRsvIDsForThisAS() - if rsv != nil { - // renewal - if index := rsv.Index(req.Index); index != nil { - return failedResponse, serrors.New("already existing e2e index", "id", req.ID, - "idx", req.Index) - } - } else { - // new setup - rsv = &e2e.Reservation{ - ID: req.ID, - SegmentReservations: make([]*segment.Reservation, len(segRsvIDs)), - } - for i, id := range segRsvIDs { - r, err := tx.GetSegmentRsvFromID(ctx, &id) - if err != nil { - return failedResponse, serrors.WrapStr("cannot get segment rsv for e2e admission", - err, "e2e_id", req.ID, "seg_id", id) - } - rsv.SegmentReservations[i] = r - } - } - if len(rsv.SegmentReservations) == 0 { - return failedResponse, serrors.New("there is no segment rsv. associated to this e2e rsv.", - "id", req.ID, "idx", req.Index) - } - - idx, err := rsv.NewIndex(req.Timestamp) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create index in e2e admission", err, - "e2e_id", req.ID) - } - index := rsv.Index(idx) - index.AllocBW = req.RequestedBW - if request.IsSuccessful() { - index.Token = &request.(*e2e.SetupReqSuccess).Token - } - - free, err := freeInSegRsv(ctx, tx, rsv.SegmentReservations[0]) - if err != nil { - return failedResponse, serrors.WrapStr("cannot compute free bw for e2e admission", err, - "e2e_id", rsv.ID) - } - free = free + rsv.AllocResv() // don't count this E2E request in the used BW - - if req.Transfer() { - // this AS must stitch two segment rsvs. according to the request - if len(segRsvIDs) == 1 { - return failedResponse, serrors.New("e2e setup request with transfer inconsistent", - "e2e_id", req.ID, "req_sgmt_rsvs_count", req.SegmentRsvASCount, - "trail_len", len(req.AllocationTrail)) - } - freeOutgoing, err := freeAfterTransfer(ctx, tx, rsv) - if err != nil { - return failedResponse, serrors.WrapStr("cannot compute transfer", err, "id", req.ID) - } - freeOutgoing += rsv.AllocResv() // do not count this rsv's BW - if free > freeOutgoing { - free = freeOutgoing - } - } - - if !request.IsSuccessful() || req.RequestedBW.ToKbps() > free { - maxWillingToAlloc := reservation.BWClsFromBW(free) - if req.Location() == e2e.Destination { - asAResponse := failedResponse.(*e2e.ResponseSetupFailure) - asAResponse.MaxBWs = append(asAResponse.MaxBWs, maxWillingToAlloc) - } else { - asARequest := &e2e.SetupReqFailure{ - SetupReq: *req, - ErrorCode: 1, - } - asARequest.AllocationTrail = append(asARequest.AllocationTrail, maxWillingToAlloc) - failedResponse = asARequest - } - return failedResponse, serrors.WrapStr("e2e not admitted", err, "id", req.ID, - "index", req.Index) - } - - // admitted so far - // TODO(juagargi) update token here - if err := tx.PersistE2ERsv(ctx, rsv); err != nil { - return failedResponse, serrors.WrapStr("cannot persist e2e reservation", err, - "id", req.ID) - } - - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, "id", req.ID) - } - - var msg base.MessageWithPath - if req.Location() == e2e.Destination { - asAResponse := failedResponse.(*e2e.ResponseSetupFailure) - msg = &e2e.ResponseSetupSuccess{ - Response: *morphE2EResponseToSuccess(&asAResponse.Response), - Token: *index.Token, - } - } else { - msg = &e2e.SetupReqSuccess{ - SetupReq: *req, - Token: *index.Token, - } - } - return msg, nil -} - -// CleanupE2EReservation will remove an index from an e2e reservation. -func (s *Store) CleanupE2EReservation(ctx context.Context, req *e2e.CleanupReq) ( - base.MessageWithPath, error) { - - if err := s.validateAuthenticators(&req.RequestMetadata); err != nil { - return nil, serrors.WrapStr("error validating request", err, "id", req.ID) - } - - response, err := s.prepareFailureE2EResp(&req.Request) - if err != nil { - return nil, serrors.WrapStr("cannot construct response", err, "id", req.ID) - } - failedResponse := &e2e.ResponseCleanupFailure{ - Response: *response, - ErrorCode: 1, - } - - tx, err := s.db.BeginTransaction(ctx, nil) - if err != nil { - return failedResponse, serrors.WrapStr("cannot create transaction", err, "id", req.ID) - } - defer tx.Rollback() - - rsv, err := tx.GetE2ERsvFromID(ctx, &req.ID) - if err != nil { - return failedResponse, serrors.WrapStr("cannot obtain e2e reservation", err, - "id", req.ID) - } - if err := rsv.RemoveIndex(req.Index); err != nil { - return failedResponse, serrors.WrapStr("cannot delete e2e reservation index", err, - "id", req.ID, "index", req.Index) - } - if err := tx.PersistE2ERsv(ctx, rsv); err != nil { - return failedResponse, serrors.WrapStr("cannot persist e2e reservation", err, - "id", req.ID) - } - if err := tx.Commit(); err != nil { - return failedResponse, serrors.WrapStr("cannot commit transaction", err, - "id", req.ID) - } - - if req.IsLastAS() { - return &e2e.ResponseCleanupSuccess{ - Response: *morphE2EResponseToSuccess(response), - }, nil - } - - return req, nil -} - -// DeleteExpiredIndices will just call the DB's method to delete the expired indices. -func (s *Store) DeleteExpiredIndices(ctx context.Context) (int, error) { - return s.db.DeleteExpiredIndices(ctx, time.Now()) -} - -// validateAuthenticators checks that the authenticators are correct. -func (s *Store) validateAuthenticators(req *base.RequestMetadata) error { - // TODO(juagargi) validate request - // DRKey authentication of request (will be left undone for later) - return nil -} - -// prepareFailureSegmentResp will create a failure segment response, which -// is sent in the reverse path that the request had. -func (s *Store) prepareFailureSegmentResp(req *segment.Request) (*segment.Response, error) { - revPath := req.Path().Copy() - if err := revPath.Reverse(); err != nil { - return nil, serrors.WrapStr("cannot reverse path for response", err) - } - - response, err := segment.NewResponse(time.Now(), &req.ID, req.Index, revPath, - false, uint8(req.Path().IndexOfCurrentHop())) - if err != nil { - return nil, serrors.WrapStr("cannot construct segment response", err) - } - return response, nil -} - -// prepareFailureE2EResp will create a failure e2e response, which -// is sent in the reverse path that the request had. -func (s *Store) prepareFailureE2EResp(req *e2e.Request) (*e2e.Response, error) { - revPath := req.Path().Copy() - if err := revPath.Reverse(); err != nil { - return nil, serrors.WrapStr("cannot reverse path for response", err) - } - - response, err := e2e.NewResponse(time.Now(), &req.ID, req.Index, revPath, - false, uint8(req.Path().IndexOfCurrentHop())) - if err != nil { - return nil, serrors.WrapStr("cannot construct e2e response", err) - } - return response, nil -} - -func morphSegmentResponseToSuccess(resp *segment.Response) *segment.Response { - resp.Accepted = true - resp.FailedHop = 0 - return resp -} - -func morphE2EResponseToSuccess(resp *e2e.Response) *e2e.Response { - resp.Accepted = true - resp.FailedHop = 0 - return resp -} - -func sumAllBW(rsvs []*e2e.Reservation) uint64 { - var accum uint64 - for _, r := range rsvs { - accum += r.AllocResv() - } - return accum -} - -func freeInSegRsv(ctx context.Context, tx backend.Transaction, segRsv *segment.Reservation) ( - uint64, error) { - - rsvs, err := tx.GetE2ERsvsOnSegRsv(ctx, &segRsv.ID) - if err != nil { - return 0, serrors.WrapStr("cannot obtain e2e reservations to compute free bw", - err, "segment_id", segRsv.ID) - } - free := float64(segRsv.ActiveIndex().AllocBW.ToKbps())*float64(segRsv.TrafficSplit) - - float64(sumAllBW(rsvs)) - return uint64(free), nil -} - -// max bw in egress interface of the transfer AS -func freeAfterTransfer(ctx context.Context, tx backend.Transaction, rsv *e2e.Reservation) ( - uint64, error) { - - seg1 := rsv.SegmentReservations[0] - seg2 := rsv.SegmentReservations[1] - if seg1.PathType == reservation.CorePath && seg2.PathType == reservation.DownPath { - // as if no transfer - return math.MaxUint64, nil - } - // get all seg rsvs with this AS as destination, AND transfer flag set - rsvs, err := tx.GetAllSegmentRsvs(ctx) - if err != nil { - return 0, err - } - var total uint64 - for _, r := range rsvs { - if r.Egress == 0 && r.PathEndProps&reservation.EndTransfer != 0 { - total += r.ActiveIndex().AllocBW.ToKbps() - } - } - ratio := float64(seg1.ActiveIndex().AllocBW.ToKbps()) / float64(total) - // effectiveE2eTraffic is the minimum BW that e2e rsvs can use - effectiveE2eTraffic := float64(seg2.ActiveIndex().AllocBW.ToKbps()) * ratio - e2es, err := tx.GetE2ERsvsOnSegRsv(ctx, &seg2.ID) - if err != nil { - return 0, err - } - total = sumAllBW(e2es) - // the available BW for this e2e rsv is the effective minus the already used - return uint64(effectiveE2eTraffic) - total, nil -} diff --git a/control/colibri/reservationstore/store_test.go b/control/colibri/reservationstore/store_test.go deleted file mode 100644 index 71b8794eae..0000000000 --- a/control/colibri/reservationstore/store_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 ETH Zurich, Anapaya Systems -// -// 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 reservationstore - -import ( - "testing" - - "github.com/scionproto/scion/control/colibri/reservationstorage" -) - -func TestStore(t *testing.T) { - var s reservationstorage.Store = &Store{} - _ = s -} diff --git a/doc/dev/design/ColibriService.md b/doc/dev/design/ColibriService.md index 0401f592b8..cf04705045 100644 --- a/doc/dev/design/ColibriService.md +++ b/doc/dev/design/ColibriService.md @@ -2,9 +2,18 @@ * Author: Juan A. García Pardo * Last updated: 2020-07-08 -* Status: **experimental**, prototype being developed in [netsec-ethz/scion:scionlab](https://github.com/netsec-ethz/scion/tree/scionlab) +* Status: **outdated**. + Prototype was developed in [netsec-ethz/scion:scionlab](https://github.com/netsec-ethz/scion/tree/scionlab) + ([commit permalink](https://github.com/netsec-ethz/scion/commit/37a556a4bc494a93fe8294ed77b6f2c9b6192746)), + to be replaced with new approach for QoS system. * Discussion at: [#3653](https://github.com/scionproto/scion/issues/3653), [#3794](https://github.com/scionproto/scion/issues/3794) +--- +⚠️ **NOTE** ⚠️
+Outdated contents! This document is kept for historical purpose. + +--- + ## Abstract This document specifies the design of the COLIBRI service. It aims for correctness not completeness, diff --git a/doc/dev/design/index.rst b/doc/dev/design/index.rst index bbc2d29548..d2a2498028 100644 --- a/doc/dev/design/index.rst +++ b/doc/dev/design/index.rst @@ -39,7 +39,6 @@ Active uri grpc - ColibriService BorderRouter .. _design-docs-completed: @@ -63,6 +62,7 @@ Outdated BeaconService PathService forwarding-key-rollover + ColibriService .. seealso:: diff --git a/nogo.json b/nogo.json index 8ab55bd98b..2724a13b9a 100644 --- a/nogo.json +++ b/nogo.json @@ -206,7 +206,6 @@ "org_golang_x_crypto/ed25519/internal/edwards25519": "", "com_github_vishvananda_netlink/nl": "", "com_github_deepmap_oapi_codegen/pkg/codegen": "", - "control/colibri/reservation/e2e/request_test.go": "", "com_github_bazelbuild_buildtools": "" } } diff --git a/pkg/experimental/colibri/reservation/BUILD.bazel b/pkg/experimental/colibri/reservation/BUILD.bazel deleted file mode 100644 index 622702e0c8..0000000000 --- a/pkg/experimental/colibri/reservation/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("//tools/lint:go.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["types.go"], - importpath = "github.com/scionproto/scion/pkg/experimental/colibri/reservation", - visibility = ["//visibility:public"], - deps = [ - "//pkg/addr:go_default_library", - "//pkg/private/serrors:go_default_library", - "//pkg/private/util:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["types_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/private/xtest:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", - ], -) diff --git a/pkg/experimental/colibri/reservation/types.go b/pkg/experimental/colibri/reservation/types.go deleted file mode 100644 index 5166dfc133..0000000000 --- a/pkg/experimental/colibri/reservation/types.go +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright 2019 ETH Zurich, Anapaya Systems -// -// 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 reservation - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "io" - "math" - "time" - - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/private/serrors" - "github.com/scionproto/scion/pkg/private/util" -) - -// SegmentID identifies a COLIBRI segment reservation. The suffix differentiates -// reservations for the same AS. -type SegmentID struct { - ASID addr.AS - Suffix [4]byte -} - -var _ io.Reader = (*SegmentID)(nil) - -const SegmentIDLen = 10 - -// NewSegmentID returns a new SegmentID -func NewSegmentID(AS addr.AS, suffix []byte) (*SegmentID, error) { - if len(suffix) != 4 { - return nil, serrors.New("wrong suffix length, should be 4", "actual_len", len(suffix)) - } - id := SegmentID{ASID: AS} - copy(id.Suffix[:], suffix) - return &id, nil -} - -// SegmentIDFromRawBuffers constructs a SegmentID from two separate buffers. -func SegmentIDFromRawBuffers(ASID, suffix []byte) (*SegmentID, error) { - if len(ASID) < 6 || len(suffix) < 4 { - return nil, serrors.New("buffers too small", "length_ASID", len(ASID), - "length_suffix", len(suffix)) - } - return NewSegmentID(addr.AS(binary.BigEndian.Uint64(append([]byte{0, 0}, ASID[:6]...))), - suffix[:4]) -} - -// SegmentIDFromRaw constructs a SegmentID parsing a raw buffer. -func SegmentIDFromRaw(raw []byte) ( - *SegmentID, error) { - - if len(raw) < SegmentIDLen { - return nil, serrors.New("buffer too small", "actual", len(raw), - "min", SegmentIDLen) - } - return SegmentIDFromRawBuffers(raw[:6], raw[6:]) -} - -// Read serializes this SegmentID into the buffer. -func (id *SegmentID) Read(raw []byte) (int, error) { - if len(raw) < SegmentIDLen { - return 0, serrors.New("buffer too small", "actual", len(raw), "min", SegmentIDLen) - } - auxBuff := make([]byte, 8) - binary.BigEndian.PutUint64(auxBuff, uint64(id.ASID)) - copy(raw, auxBuff[2:8]) - copy(raw[6:], id.Suffix[:]) - return SegmentIDLen, nil -} - -// ToRaw calls Read and returns a new allocated buffer with the ID serialized. -func (id *SegmentID) ToRaw() []byte { - buf := make([]byte, SegmentIDLen) - _, _ = id.Read(buf) // safely ignore errors as they can only come from buffer size - return buf -} - -func (id *SegmentID) String() string { - return fmt.Sprintf("%s-%x", id.ASID, id.Suffix) -} - -// E2EID identifies a COLIBRI E2E reservation. The suffix is different for each -// reservation for any given AS. -type E2EID struct { - ASID addr.AS - Suffix [10]byte -} - -const E2EIDLen = 16 - -var _ io.Reader = (*E2EID)(nil) - -// NewE2EID returns a new E2EID -func NewE2EID(AS addr.AS, suffix []byte) (*E2EID, error) { - if len(suffix) != 10 { - return nil, serrors.New("wrong suffix length, should be 10", "actual_len", len(suffix)) - } - id := E2EID{ASID: AS} - copy(id.Suffix[:], suffix) - return &id, nil -} - -// E2EIDFromRawBuffers constructs a E2DID from two separate buffers. -func E2EIDFromRawBuffers(ASID, suffix []byte) (*E2EID, error) { - if len(ASID) < 6 || len(suffix) < 10 { - return nil, serrors.New("buffers too small", "length_ASID", len(ASID), - "length_suffix", len(suffix)) - } - return NewE2EID(addr.AS(binary.BigEndian.Uint64(append([]byte{0, 0}, ASID[:6]...))), - suffix[:10]) -} - -// E2EIDFromRaw constructs an E2EID parsing a buffer. -func E2EIDFromRaw(raw []byte) (*E2EID, error) { - if len(raw) < E2EIDLen { - return nil, serrors.New("buffer too small", "actual", len(raw), "min", E2EIDLen) - } - return E2EIDFromRawBuffers(raw[:6], raw[6:]) -} - -// Read serializes this E2EID into the buffer. -func (id *E2EID) Read(raw []byte) (int, error) { - if len(raw) < E2EIDLen { - return 0, serrors.New("buffer too small", "actual", len(raw), "min", E2EIDLen) - } - auxBuff := make([]byte, 8) - binary.BigEndian.PutUint64(auxBuff, uint64(id.ASID)) - copy(raw, auxBuff[2:8]) - copy(raw[6:], id.Suffix[:]) - return E2EIDLen, nil -} - -// ToRaw calls Read and returns a new allocated buffer with the ID serialized. -func (id *E2EID) ToRaw() []byte { - buf := make([]byte, E2EIDLen) - _, _ = id.Read(buf) // safely ignore errors as they can only come from buffer size - return buf -} - -// Tick represents a slice of time of 4 seconds. -type Tick uint32 - -// TickFromTime returns the tick for a given time. -func TickFromTime(t time.Time) Tick { - return Tick(util.TimeToSecs(t) / 4) -} - -func (t Tick) ToTime() time.Time { - return util.SecsToTime(uint32(t) * 4) -} - -// BWCls is the bandwidth class. bandwidth = 16 * sqrt(2^(BWCls - 1)). 0 <= bwcls <= 63 kbps. -type BWCls uint8 - -// BWClsFromBW constructs a BWCls from the bandwidth. Given that -// bandwidth = 16 * sqrt(2^(BWCls - 1)) -// where bandwidth is kbps. We then have -// BWCls = 2 * log2( bandwidth/16 ) + 1 -// The value of BWCls will be the ceiling of the previous expression. -func BWClsFromBW(bwKbps uint64) BWCls { - cls := 2*math.Log2(float64(bwKbps)/16) + 1 - cls = math.Min(cls, 63) - return BWCls(math.Ceil(cls)) -} - -// Validate will return an error for invalid values. -func (b BWCls) Validate() error { - if b > 63 { - return serrors.New("invalid BWClass value", "bw_cls", b) - } - return nil -} - -// ToKbps returns the kilobits per second this BWCls represents. -func (b BWCls) ToKbps() uint64 { - return uint64(16 * math.Sqrt(math.Pow(2, float64(b)-1))) -} - -// MaxBWCls returns the maximum of two BWCls. -func MaxBWCls(a, b BWCls) BWCls { - if a > b { - return a - } - return b -} - -// MinBWCls returns the minimum of two BWCls. -func MinBWCls(a, b BWCls) BWCls { - if a < b { - return a - } - return b -} - -// SplitCls is the traffic split parameter. split = sqrt(2^c). The split divides the bandwidth -// in control traffic (BW * split) and end to end traffic (BW * (1-s)). 0 <= splitCls <= 256 . -type SplitCls uint8 - -// RLC Request Latency Class. latency = 2^rlc miliseconds. 0 <= rlc <= 63 -type RLC uint8 - -// Validate will return an error for invalid values. -func (c RLC) Validate() error { - if c > 63 { - return serrors.New("invalid BWClass", "bw_cls", c) - } - return nil -} - -// IndexNumber is a 4 bit index for a reservation. -type IndexNumber uint8 - -// Validate will return an error for invalid values. -func (i IndexNumber) Validate() error { - if i >= 1<<4 { - return serrors.New("invalid IndexNumber", "value", i) - } - return nil -} - -func (i IndexNumber) Add(other IndexNumber) IndexNumber { - return (i + other) % 16 -} - -func (i IndexNumber) Sub(other IndexNumber) IndexNumber { - return (i - other) % 16 -} - -// PathType specifies which type of COLIBRI path this segment reservation or request refers to. -type PathType uint8 - -// the different COLIBRI path types. -const ( - UnknownPath PathType = iota - DownPath - UpPath - PeeringDownPath - PeeringUpPath - E2EPath - CorePath -) - -// Validate will return an error for invalid values. -func (pt PathType) Validate() error { - if pt == UnknownPath || pt > CorePath { - return serrors.New("invalid path type", "path_type", pt) - } - return nil -} - -// InfoField is used in the reservation token and segment request data. -// 0B 1 2 3 4 5 6 7 -// +--------+--------+--------+--------+--------+--------+--------+--------+ -// | Expiration time (4B) | BwCls | RTT Cls|Idx|Type| padding| -// +--------+--------+--------+--------+--------+--------+--------+--------+ -// -// The bandwidth class (BwCls) indicates the reserved bandwidth in an active -// reservation. In a steady request, it indicates the minimal bandwidth class -// reserved so far. In a ephemeral request, it indicates the bandwidth class -// that the source end host is seeking to reserve. -// -// The round trip class (RTT Cls) allows for more granular control in the -// pending request garbage collection. -// -// The reservation index (Idx) is used to allow for multiple overlapping -// reservations within a single path, which enables renewal and changing the -// bandwidth requested. -// -// Type indicates which path type of the reservation. -type InfoField struct { - ExpirationTick Tick - BWCls BWCls - RLC RLC - Idx IndexNumber - PathType PathType -} - -// InfoFieldLen is the length in bytes of the InfoField. -const InfoFieldLen = 8 - -var _ io.Reader = (*InfoField)(nil) - -// Validate will return an error for invalid values. -func (f *InfoField) Validate() error { - if err := f.BWCls.Validate(); err != nil { - return err - } - if err := f.RLC.Validate(); err != nil { - return err - } - if err := f.Idx.Validate(); err != nil { - return err - } - if err := f.PathType.Validate(); err != nil { - return err - } - - return nil -} - -// InfoFieldFromRaw builds an InfoField from the InfoFieldLen bytes buffer. -func InfoFieldFromRaw(raw []byte) (*InfoField, error) { - if len(raw) < InfoFieldLen { - return nil, serrors.New("buffer too small", "min_size", InfoFieldLen, - "current_size", len(raw)) - } - info := InfoField{ - ExpirationTick: Tick(binary.BigEndian.Uint32(raw[:4])), - BWCls: BWCls(raw[4]), - RLC: RLC(raw[5]), - Idx: IndexNumber(raw[6]) >> 4, - PathType: PathType(raw[6]) & 0x7, - } - if err := info.Validate(); err != nil { - return nil, err - } - return &info, nil -} - -// Read serializes this InfoField into a sequence of InfoFieldLen bytes. -func (f *InfoField) Read(b []byte) (int, error) { - if len(b) < InfoFieldLen { - return 0, serrors.New("buffer too small", "min_size", InfoFieldLen, - "current_size", len(b)) - } - binary.BigEndian.PutUint32(b[:4], uint32(f.ExpirationTick)) - b[4] = byte(f.BWCls) - b[5] = byte(f.RLC) - b[6] = byte(f.Idx<<4) | uint8(f.PathType) - b[7] = 0 // b[7] is padding - return InfoFieldLen, nil -} - -// ToRaw returns the serial representation of the InfoField. -func (f *InfoField) ToRaw() []byte { - var buff []byte = nil - if f != nil { - buff = make([]byte, InfoFieldLen) - _, _ = f.Read(buff) // safely ignore errors as they can only come from buffer size - } - return buff -} - -// PathEndProps represent the zero or more properties a COLIBRI path can have at both ends. -type PathEndProps uint8 - -// The only current properties are "Local" (can be used to create e2e rsvs) and "Transfer" (can be -// stiched together with another segment reservation). The first 4 bits encode the properties -// of the "Start" AS, and the last 4 bits encode those of the "End" AS. -const ( - StartLocal PathEndProps = 0x10 - StartTransfer PathEndProps = 0x20 - EndLocal PathEndProps = 0x01 - EndTransfer PathEndProps = 0x02 -) - -// Validate will return an error for invalid values. -func (pep PathEndProps) Validate() error { - if pep&0x0F > 0x03 { - return serrors.New("invalid path end properties (@End)", "path_end_props", pep) - } - if pep>>4 > 0x03 { - return serrors.New("invalid path end properties (@Start)", "path_end_props", pep) - } - return nil -} - -// ValidateWithPathType checks the validity of the properties when in a path of the specified type. -func (pep PathEndProps) ValidateWithPathType(pt PathType) error { - if err := pep.Validate(); err != nil { - return err - } - var invalid bool - s := pep & 0xF0 - e := pep & 0x0F - switch pt { - case CorePath: - if s == 0 { - invalid = true - } - case UpPath: - if s != StartLocal { - invalid = true - } - case DownPath: - if e != EndLocal { - invalid = true - } - case PeeringUpPath: - if s != StartLocal || e == 0 { - invalid = true - } - case PeeringDownPath: - if e != EndLocal || s == 0 { - invalid = true - } - } - if invalid { - return serrors.New("invalid combination of path end properties and path type", - "end_properties", hex.EncodeToString([]byte{byte(pep)}), "path_type", pt) - } - return nil -} - -func NewPathEndProps(startLocal, startTransfer, endLocal, endTransfer bool) PathEndProps { - var props PathEndProps - if startLocal { - props |= StartLocal - } - if startTransfer { - props |= StartTransfer - } - if endLocal { - props |= EndLocal - } - if endTransfer { - props |= EndTransfer - } - return props -} - -// AllocationBead represents an allocation resolved in an AS for a given reservation. -// It is used in an array to represent the allocation trail that happened for a reservation. -type AllocationBead struct { - AllocBW BWCls - MaxBW BWCls -} - -type AllocationBeads []AllocationBead - -// MinMax returns the minimum of all the max BW in the AllocationBeads. -func (bs AllocationBeads) MinMax() BWCls { - if len(bs) == 0 { - return 0 - } - var min BWCls = math.MaxUint8 - for _, b := range bs { - if b.MaxBW < min { - min = b.MaxBW - } - } - return min -} - -// HopField is a COLIBRI HopField. -// TODO(juagargi) move to slayers -type HopField struct { - Ingress uint16 - Egress uint16 - Mac [4]byte -} - -const HopFieldLen = 8 - -var _ io.Reader = (*HopField)(nil) - -// HopFieldFromRaw builds a HopField from a raw buffer. -func HopFieldFromRaw(raw []byte) (*HopField, error) { - if len(raw) < HopFieldLen { - return nil, serrors.New("buffer too small for HopField", "min_size", HopFieldLen, - "current_size", len(raw)) - } - hf := HopField{ - Ingress: binary.BigEndian.Uint16(raw[:2]), - Egress: binary.BigEndian.Uint16(raw[2:4]), - } - copy(hf.Mac[:], raw[4:8]) - return &hf, nil -} - -// Read serializes this HopField into the buffer. -func (hf *HopField) Read(b []byte) (int, error) { - if len(b) < HopFieldLen { - return 0, serrors.New("buffer too small for HopField", "min_size", HopFieldLen, - "current_size", len(b)) - } - binary.BigEndian.PutUint16(b[:2], hf.Ingress) - binary.BigEndian.PutUint16(b[2:4], hf.Egress) - copy(b[4:], hf.Mac[:]) - return HopFieldLen, nil -} - -// ToRaw returns the serial representation of the HopField. -func (hf *HopField) ToRaw() []byte { - buff := make([]byte, HopFieldLen) - _, _ = hf.Read(buff) // discard returned values - return buff -} - -// Token is used in the data plane to forward COLIBRI packets. -type Token struct { - InfoField - HopFields []HopField -} - -var _ io.Reader = (*Token)(nil) - -// Validate will return an error for invalid values. It will not check the hop fields' validity. -func (t *Token) Validate() error { - if len(t.HopFields) == 0 { - return serrors.New("token without hop fields") - } - return t.InfoField.Validate() -} - -// TokenFromRaw builds a Token from the passed bytes buffer. -func TokenFromRaw(raw []byte) (*Token, error) { - if raw == nil { - return nil, nil - } - rawHFs := len(raw) - InfoFieldLen - if rawHFs < 0 || rawHFs%HopFieldLen != 0 { - return nil, serrors.New("buffer too small for Token", "min_size", InfoFieldLen, - "current_size", len(raw)) - } - numHFs := rawHFs / HopFieldLen - inf, err := InfoFieldFromRaw(raw[:InfoFieldLen]) - if err != nil { - return nil, err - } - t := Token{ - InfoField: *inf, - } - if numHFs > 0 { - t.HopFields = make([]HopField, numHFs) - } - for i := 0; i < numHFs; i++ { - offset := InfoFieldLen + i*HopFieldLen - hf, err := HopFieldFromRaw(raw[offset : offset+HopFieldLen]) - if err != nil { - return nil, err - } - t.HopFields[i] = *hf - } - return &t, nil -} - -// Len returns the number of bytes of this token if serialized. -func (t *Token) Len() int { - if t == nil { - return 0 - } - return InfoFieldLen + len(t.HopFields)*HopFieldLen -} - -// Read serializes this Token to the passed buffer. -func (t *Token) Read(b []byte) (int, error) { - length := t.Len() - if len(b) < length { - return 0, serrors.New("buffer too small", "min_size", length, "current_size", len(b)) - } - offset, err := t.InfoField.Read(b[:InfoFieldLen]) - if err != nil { - return 0, err - } - for i := 0; i < len(t.HopFields); i++ { - _, _ = t.HopFields[i].Read(b[offset : offset+HopFieldLen]) - offset += HopFieldLen - } - return offset, nil -} - -// ToRaw returns the serial representation of the Token. -func (t *Token) ToRaw() []byte { - var buff []byte = nil - if t != nil { - buff = make([]byte, t.Len()) - _, _ = t.Read(buff) // safely ignore errors as they can only come from buffer size - } - return buff -} diff --git a/pkg/experimental/colibri/reservation/types_test.go b/pkg/experimental/colibri/reservation/types_test.go deleted file mode 100644 index df4969af61..0000000000 --- a/pkg/experimental/colibri/reservation/types_test.go +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2019 ETH Zurich, Anapaya Systems -// -// 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 reservation - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/pkg/private/xtest" -) - -func TestSegmentIDFromRaw(t *testing.T) { - id, err := SegmentIDFromRaw(xtest.MustParseHexString("ffaa00001101facecafe")) - require.NoError(t, err) - require.Equal(t, xtest.MustParseAS("ffaa:0:1101"), id.ASID) - require.Equal(t, xtest.MustParseHexString("facecafe"), id.Suffix[:]) -} - -func TestSegmentIDRead(t *testing.T) { - reference := SegmentID{ - ASID: xtest.MustParseAS("ffaa:0:1101"), - } - copy(reference.Suffix[:], xtest.MustParseHexString("facecafe")) - raw := make([]byte, SegmentIDLen) - n, err := reference.Read(raw) - require.NoError(t, err) - require.Equal(t, SegmentIDLen, n) - require.Equal(t, xtest.MustParseHexString("ffaa00001101facecafe"), raw) -} - -func TestSegmentIDString(t *testing.T) { - cases := []struct { - ID SegmentID - Str string - }{ - {ID: mustParseSegmentID("ff0000001101facecafe"), Str: "ff00:0:1101-facecafe"}, - {ID: mustParseSegmentID("ff000000110100000000"), Str: "ff00:0:1101-00000000"}, - } - for i, c := range cases { - name := fmt.Sprintf("case %d", i) - t.Run(name, func(t *testing.T) { - c := c - t.Parallel() - require.Equal(t, c.Str, c.ID.String()) - }) - } -} - -func TestE2EIDFromRaw(t *testing.T) { - raw := xtest.MustParseHexString("ffaa00001101facecafedeadbeeff00d") - id, err := E2EIDFromRaw(raw) - require.NoError(t, err) - require.Equal(t, xtest.MustParseAS("ffaa:0:1101"), id.ASID) - require.Equal(t, xtest.MustParseHexString("facecafedeadbeeff00d"), id.Suffix[:]) -} - -func TestE2EIDRead(t *testing.T) { - reference := E2EID{ - ASID: xtest.MustParseAS("ffaa:0:1101"), - } - copy(reference.Suffix[:], xtest.MustParseHexString("facecafedeadbeeff00d")) - raw := make([]byte, E2EIDLen) - n, err := reference.Read(raw) - require.NoError(t, err) - require.Equal(t, E2EIDLen, n) - require.Equal(t, xtest.MustParseHexString("ffaa00001101facecafedeadbeeff00d"), raw) -} - -func TestTickFromTime(t *testing.T) { - require.Equal(t, Tick(0), TickFromTime(time.Unix(0, 0))) - require.Equal(t, Tick(0), TickFromTime(time.Unix(3, 999999))) - require.Equal(t, Tick(1), TickFromTime(time.Unix(4, 0))) -} - -func TestTickToTime(t *testing.T) { - require.Equal(t, time.Unix(0, 0), Tick(0).ToTime()) - require.Equal(t, time.Unix(4, 0), Tick(1).ToTime()) - require.Equal(t, time.Unix(0, 0), TickFromTime(time.Unix(0, 0)).ToTime()) - require.Equal(t, time.Unix(0, 0), TickFromTime(time.Unix(3, 999999)).ToTime()) - require.Equal(t, time.Unix(4, 0), TickFromTime(time.Unix(4, 0)).ToTime()) -} - -func TestValidateBWCls(t *testing.T) { - for i := 0; i < 64; i++ { - c := BWCls(i) - err := c.Validate() - require.NoError(t, err) - } - c := BWCls(64) - err := c.Validate() - require.Error(t, err) -} - -func TestBWClsToKbps(t *testing.T) { - cases := map[BWCls]uint64{ - 0: 11, - 1: 16, - 2: 22, - 5: 64, - 13: 1024, - 63: 32 * 1024 * 1024 * 1024, // 32 TBps - } - for cls, bw := range cases { - name := fmt.Sprintf("case for %d", cls) - t.Run(name, func(t *testing.T) { - cls := cls - bw := bw - t.Parallel() - require.Equal(t, bw, cls.ToKbps()) - }) - } -} - -func TestBWClsFromBW(t *testing.T) { - cases := map[uint64]BWCls{ - 0: 0, - 16: 1, - 22: 2, - 64: 5, - 1024: 13, - 32 * 1024 * 1024 * 1024: 63, - 21: 2, - 4096: 17, - 4000: 17, - 4097: 18, - } - for bw, cls := range cases { - name := fmt.Sprintf("case for %d", bw) - t.Run(name, func(t *testing.T) { - bw := bw - cls := cls - t.Parallel() - require.Equal(t, cls, BWClsFromBW(bw)) - }) - } -} - -func TestMaxBWCls(t *testing.T) { - cases := []struct{ a, b, max BWCls }{ - {a: 1, b: 1, max: 1}, - {a: 0, b: 1, max: 1}, - {a: 255, b: 1, max: 255}, - } - for i, c := range cases { - name := fmt.Sprintf("case %d", i) - t.Run(name, func(t *testing.T) { - c := c - t.Parallel() - require.Equal(t, c.max, MaxBWCls(c.a, c.b)) - }) - } -} - -func TestMinBWCls(t *testing.T) { - cases := []struct{ a, b, min BWCls }{ - {a: 1, b: 1, min: 1}, - {a: 0, b: 1, min: 0}, - {a: 255, b: 0, min: 0}, - } - for i, c := range cases { - name := fmt.Sprintf("case %d", i) - t.Run(name, func(t *testing.T) { - c := c - t.Parallel() - require.Equal(t, c.min, MinBWCls(c.a, c.b)) - }) - } -} - -func TestValidateRLC(t *testing.T) { - for i := 0; i < 64; i++ { - c := RLC(i) - err := c.Validate() - require.NoError(t, err) - } - c := RLC(64) - err := c.Validate() - require.Error(t, err) -} - -func TestValidateIndexNumber(t *testing.T) { - for i := 0; i < 16; i++ { - idx := IndexNumber(i) - err := idx.Validate() - require.NoError(t, err) - } - idx := IndexNumber(16) - err := idx.Validate() - require.Error(t, err) -} - -func TestIndexNumberArithmetic(t *testing.T) { - var idx IndexNumber = 1 - x := idx.Add(IndexNumber(15)) - require.Equal(t, IndexNumber(0), x) - x = idx.Sub(IndexNumber(2)) - require.Equal(t, IndexNumber(15), x) - // distance from 2 to 0 = 0 - 2 = 14 mod 16 - x = IndexNumber(2) - distance := IndexNumber(0).Sub(x) - require.Equal(t, IndexNumber(14), distance) - // distance from 2 to 4 - distance = IndexNumber(4).Sub(x) - require.Equal(t, IndexNumber(2), distance) -} - -func TestValidatePathType(t *testing.T) { - validTypes := []PathType{ - DownPath, - UpPath, - PeeringDownPath, - PeeringUpPath, - E2EPath, - CorePath, - } - for _, vt := range validTypes { - err := vt.Validate() - require.NoError(t, err) - } - err := UnknownPath.Validate() - require.Error(t, err) - - pt := CorePath + 1 - err = pt.Validate() - require.Error(t, err) -} - -func TestValidateInfoField(t *testing.T) { - infoField := InfoField{ - ExpirationTick: 0, - BWCls: 0, - RLC: 0, - Idx: 0, - PathType: CorePath, - } - err := infoField.Validate() - require.NoError(t, err) - - otherIF := infoField - otherIF.BWCls = 64 - err = otherIF.Validate() - require.Error(t, err) - - otherIF = infoField - otherIF.RLC = 64 - err = otherIF.Validate() - require.Error(t, err) - - otherIF = infoField - otherIF.Idx = 16 - err = otherIF.Validate() - require.Error(t, err) - - otherIF = infoField - otherIF.PathType = CorePath + 1 - err = otherIF.Validate() - require.Error(t, err) -} - -func TestInfoFieldFromRaw(t *testing.T) { - reference := newInfoField() - rawReference := newInfoFieldRaw() - info, err := InfoFieldFromRaw(rawReference) - require.NoError(t, err) - require.Equal(t, reference, *info) -} - -func TestInfoFieldRead(t *testing.T) { - reference := newInfoField() - rawReference := newInfoFieldRaw() - raw := make([]byte, InfoFieldLen) - // pollute the buffer with garbage - for i := 0; i < InfoFieldLen; i++ { - raw[i] = byte(i % 256) - } - n, err := reference.Read(raw) - require.NoError(t, err) - require.Equal(t, InfoFieldLen, n) - require.Equal(t, rawReference, raw) -} - -func TestInfoFieldToRaw(t *testing.T) { - val := newInfoField() - reference := &val - rawReference := newInfoFieldRaw() - require.Equal(t, rawReference, reference.ToRaw()) - reference = nil - require.Equal(t, ([]byte)(nil), reference.ToRaw()) -} - -func TestValidatePathEndProperties(t *testing.T) { - for i := 0; i < 4; i++ { - pep := PathEndProps(i) - err := pep.Validate() - require.NoError(t, err) - } - pep := PathEndProps(4) - err := pep.Validate() - require.Error(t, err) - - for i := 0; i < 4; i++ { - pep := PathEndProps(i << 4) - err := pep.Validate() - require.NoError(t, err) - } - pep = PathEndProps(4 << 4) - err = pep.Validate() - require.Error(t, err) - - pep = PathEndProps(0x10 | 0x04) - err = pep.Validate() - require.Error(t, err) -} - -func TestValidatePathEndPropsWithPathType(t *testing.T) { - cases := []struct { - PT PathType - EP PathEndProps - Valid bool - }{ - // core path - {CorePath, StartLocal | EndLocal, true}, - {CorePath, StartLocal | EndLocal | EndTransfer, true}, - {CorePath, StartTransfer | EndTransfer, true}, - {CorePath, StartLocal, true}, - {CorePath, StartTransfer, true}, - {CorePath, EndLocal, false}, - {CorePath, 0, false}, - // up path - {UpPath, StartLocal, true}, - {UpPath, StartLocal | EndLocal | EndTransfer, true}, - {UpPath, 0, false}, - {UpPath, StartTransfer, false}, - {UpPath, StartTransfer | StartLocal, false}, - // down path - {DownPath, EndLocal, true}, - {DownPath, EndLocal | StartLocal | StartTransfer, true}, - {DownPath, 0, false}, - {DownPath, EndTransfer, false}, - {DownPath, EndTransfer | EndLocal, false}, - // peering up path - {PeeringUpPath, StartLocal | EndLocal, true}, - {PeeringUpPath, StartLocal | EndLocal | EndTransfer, true}, - {PeeringUpPath, 0, false}, - {PeeringUpPath, StartLocal, false}, - {PeeringUpPath, StartLocal | StartTransfer | EndLocal, false}, - {PeeringUpPath, StartTransfer | EndLocal, false}, - {PeeringUpPath, EndLocal, false}, - // peering down path - {PeeringDownPath, EndLocal | StartLocal, true}, - {PeeringDownPath, EndLocal | StartLocal | StartTransfer, true}, - {PeeringDownPath, 0, false}, - {PeeringDownPath, EndLocal, false}, - {PeeringDownPath, EndLocal | EndTransfer | StartLocal, false}, - {PeeringDownPath, EndTransfer | StartLocal, false}, - {PeeringDownPath, StartLocal, false}, - } - for i, c := range cases { - name := fmt.Sprintf("iteration %d", i) - t.Run(name, func(t *testing.T) { - c := c - t.Parallel() - err := c.EP.ValidateWithPathType(c.PT) - if c.Valid { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} - -func TestAllocationBeadsMinMax(t *testing.T) { - cases := []struct { - Trail AllocationBeads - Min BWCls - }{ - {newAllocationBeads(), 0}, - {newAllocationBeads(0, 1), 1}, - {newAllocationBeads(0, 3, 0, 1), 1}, - {newAllocationBeads(0, 3, 0, 255), 3}, - } - for i, c := range cases { - name := fmt.Sprintf("iteration %d", i) - t.Run(name, func(t *testing.T) { - c := c - t.Parallel() - require.Equal(t, c.Min, c.Trail.MinMax()) - }) - } -} - -func TestValidateToken(t *testing.T) { - tok := newToken() - err := tok.Validate() - require.NoError(t, err) - tok.HopFields = []HopField{} - err = tok.Validate() - require.Error(t, err) -} - -func TestTokenLen(t *testing.T) { - tok := newToken() - require.Equal(t, len(newTokenRaw()), tok.Len()) -} - -func TestTokenFromRaw(t *testing.T) { - referenceRaw := newTokenRaw() - reference := newToken() - tok, err := TokenFromRaw(referenceRaw) - require.NoError(t, err) - require.Equal(t, reference, *tok) - - // buffer too small - _, err = TokenFromRaw(referenceRaw[:3]) - require.Error(t, err) - - // one hop field less - tok, err = TokenFromRaw(referenceRaw[:len(referenceRaw)-HopFieldLen]) - require.NoError(t, err) - require.Len(t, tok.HopFields, len(reference.HopFields)-1) -} -func TestTokenRead(t *testing.T) { - tok := newToken() - rawReference := newTokenRaw() - buf := make([]byte, len(rawReference)) - c, err := tok.Read(buf) - require.NoError(t, err) - require.Equal(t, len(buf), c) - require.Equal(t, rawReference, buf) - - // buffer too small - _, err = tok.Read(buf[:len(rawReference)-1]) - require.Error(t, err) -} - -func TestTokenToRaw(t *testing.T) { - tok := newToken() - raw := newTokenRaw() - require.Equal(t, raw, tok.ToRaw()) -} - -func newInfoField() InfoField { - return InfoField{ - ExpirationTick: 384555855, - BWCls: 13, - RLC: 4, - Idx: 2, - PathType: E2EPath, - } -} - -func newInfoFieldRaw() []byte { - return xtest.MustParseHexString("16ebdb4f0d042500") -} - -func newHopField(ingress, egress uint16, mac []byte) *HopField { - hf := HopField{ - Ingress: ingress, - Egress: egress, - } - if len(mac) < len(hf.Mac) { - panic(fmt.Errorf("mac is too short: %d", len(mac))) - } - copy(hf.Mac[:], mac) - return &hf -} - -func newToken() Token { - return Token{ - InfoField: newInfoField(), - HopFields: []HopField{ - *newHopField(1, 2, xtest.MustParseHexString("badcffee")), - *newHopField(1, 2, xtest.MustParseHexString("baadf00d")), - }, - } -} -func newTokenRaw() []byte { - return xtest.MustParseHexString("16ebdb4f0d04250000010002badcffee00010002baadf00d") -} - -func mustParseSegmentID(s string) SegmentID { - id, err := SegmentIDFromRaw(xtest.MustParseHexString(s)) - if err != nil { - panic(err) - } - return *id -} - -// newAllocationBeads (1,2,3,4) returns two beads {alloc: 1, max: 2}, {alloc:3, max:4} -func newAllocationBeads(beads ...BWCls) AllocationBeads { - if len(beads)%2 != 0 { - panic("must have an even number of parameters") - } - ret := make(AllocationBeads, len(beads)/2) - for i := 0; i < len(beads); i += 2 { - ret[i/2] = AllocationBead{AllocBW: beads[i], MaxBW: beads[i+1]} - } - return ret -} diff --git a/tools/topogen.py b/tools/topogen.py index c01a1cc7b3..209dc75e08 100755 --- a/tools/topogen.py +++ b/tools/topogen.py @@ -47,8 +47,6 @@ def add_arguments(parser): parser.add_argument('--sig', action='store_true', help='Generate a SIG per AS (only available with -d, the SIG image needs\ to be built manually e.g. when running acceptance tests)') - parser.add_argument('-qos', '--colibri', action='store_true', - help='Generate COLIBRI service') parser.add_argument('--features', help='Feature flags to enable, a comma separated list\ e.g. foo,bar enables foo and bar feature.') return parser diff --git a/tools/topology/common.py b/tools/topology/common.py index b12326da03..934725effe 100644 --- a/tools/topology/common.py +++ b/tools/topology/common.py @@ -30,14 +30,12 @@ "control_service", "discovery_service", "border_routers", - "colibri_service", ) BR_CONFIG_NAME = 'br.toml' BS_CONFIG_NAME = 'bs.toml' CS_CONFIG_NAME = 'cs.toml' PS_CONFIG_NAME = 'ps.toml' -CO_CONFIG_NAME = 'co.toml' SD_CONFIG_NAME = 'sd.toml' DISP_CONFIG_NAME = 'disp.toml' SIG_CONFIG_NAME = 'sig.toml' diff --git a/tools/topology/config.py b/tools/topology/config.py index 948ed97f80..5fcabb91c9 100644 --- a/tools/topology/config.py +++ b/tools/topology/config.py @@ -130,7 +130,6 @@ def _generate_go(self, topo_dicts): go_gen.generate_br() go_gen.generate_sciond() go_gen.generate_control_service() - go_gen.generate_co() go_gen.generate_disp() def _go_args(self, topo_dicts): diff --git a/tools/topology/go.py b/tools/topology/go.py index 85e4c63718..36c82537e2 100644 --- a/tools/topology/go.py +++ b/tools/topology/go.py @@ -19,7 +19,6 @@ # Stdlib import os import toml -import yaml from typing import Mapping # SCION @@ -35,7 +34,6 @@ translate_features, SD_API_PORT, SD_CONFIG_NAME, - CO_CONFIG_NAME, ) from topology.net import socket_address_str, NetworkDescription, IPNetwork @@ -45,11 +43,8 @@ DEFAULT_BR_PROM_PORT, SCIOND_PROM_PORT, DISP_PROM_PORT, - CO_PROM_PORT, ) -DEFAULT_COLIBRI_TOTAL_BW = 1000 - class GoGenArgs(ArgsBase): def __init__(self, args, topo_config, topo_dicts, @@ -140,96 +135,6 @@ def _build_control_service_conf(self, topo_id, ia, base, name, infra_elem, ca): raw_entry['ca'] = {'mode': 'in-process'} return raw_entry - def generate_co(self): - if not self.args.colibri: - return - for topo_id, topo in self.args.topo_dicts.items(): - for elem_id, elem in topo.get("colibri_service", {}).items(): - # only a single Go-CO per AS is currently supported - if elem_id.endswith("-1"): - base = topo_id.base_dir(self.args.output_dir) - co_conf = self._build_co_conf(topo_id, topo["isd_as"], base, elem_id, elem) - write_file(os.path.join(base, elem_id, CO_CONFIG_NAME), toml.dumps(co_conf)) - traffic_matrix = self._build_co_traffic_matrix(topo_id) - write_file(os.path.join(base, elem_id, 'matrix.yml'), - yaml.dump(traffic_matrix, default_flow_style=False)) - rsvps = self._build_co_reservations(topo_id) - write_file(os.path.join(base, elem_id, 'reservations.yml'), - yaml.dump(rsvps, default_flow_style=False)) - - def _build_co_conf(self, topo_id, ia, base, name, infra_elem): - config_dir = '/share/conf' if self.args.docker else base - raw_entry = { - 'general': { - 'ID': name, - 'ConfigDir': config_dir, - 'ReconnectToDispatcher': True, - }, - 'log': self._log_entry(name), - 'trust_db': { - 'connection': os.path.join(self.db_dir, '%s.trust.db' % name), - }, - 'tracing': self._tracing_entry(), - 'metrics': self._metrics_entry(infra_elem, CO_PROM_PORT), - 'features': translate_features(self.args.features), - } - return raw_entry - - def _build_co_traffic_matrix(self, ia): - """ - Creates a NxN traffic matrix for colibri with N = len(interfaces) - """ - topo = self.args.topo_dicts[ia] - if_ids = {iface for br in topo['border_routers'].values() for iface in br['interfaces']} - if_ids.add(0) - bw = int(DEFAULT_COLIBRI_TOTAL_BW / (len(if_ids) - 1)) - traffic_matrix = {} - for inIfid in if_ids: - traffic_matrix[inIfid] = {} - for egIfid in if_ids.difference({inIfid}): - traffic_matrix[inIfid][egIfid] = bw - return traffic_matrix - - def _build_co_reservations(self, ia): - """ - Generates a dictionary of reservations with one entry per core AS (if "ia" is core) - excluding itself, or a pair (up and down) per core AS in the ISD if "ia" is not core. - """ - rsvps = {} - this_as = self.args.topo_dicts[ia] - if this_as['Core']: - for dst_ia, topo in self.args.topo_dicts.items(): - if dst_ia != ia and topo['Core']: - rsvps['Core-%s' % dst_ia] = self._build_co_reservation(dst_ia, 'Core') - else: - for dst_ia, topo in self.args.topo_dicts.items(): - if dst_ia != ia and dst_ia._isd == ia._isd and topo['Core']: - # reach this core AS in the same ISD - rsvps['Up-%s' % dst_ia] = self._build_co_reservation(dst_ia, 'Up') - rsvps['Down-%s' % dst_ia] = self._build_co_reservation(dst_ia, 'Down') - return rsvps - - def _build_co_reservation(self, dst_ia, path_type): - start_props = {'L', 'T'} - end_props = {'L', 'T'} - if path_type == 'Up': - start_props.remove('T') - elif path_type == 'Down': - end_props.remove('T') - return { - 'desired_size': 27, - 'ia': str(dst_ia), - 'max_size': 30, - 'min_size': 1, - 'path_predicate': '%s#0' % dst_ia, - 'path_type': path_type, - 'split_cls': 8, - 'end_props': { - 'start': list(start_props), - 'end': list(end_props) - } - } - def generate_sciond(self): for topo_id, topo in self.args.topo_dicts.items(): base = topo_id.base_dir(self.args.output_dir) diff --git a/tools/topology/prometheus.py b/tools/topology/prometheus.py index 94cc932fff..9a896a64fc 100644 --- a/tools/topology/prometheus.py +++ b/tools/topology/prometheus.py @@ -41,7 +41,6 @@ CS_PROM_PORT = 30452 SCIOND_PROM_PORT = 30455 SIG_PROM_PORT = 30456 -CO_PROM_PORT = 30457 DISP_PROM_PORT = 30441 DEFAULT_BR_PROM_PORT = 30442 diff --git a/tools/topology/topo.py b/tools/topology/topo.py index 46f8c9a58f..e92f8106cf 100644 --- a/tools/topology/topo.py +++ b/tools/topology/topo.py @@ -52,7 +52,6 @@ DEFAULT_BEACON_SERVERS = 1 DEFAULT_CONTROL_SERVERS = 1 -DEFAULT_COLIBRI_SERVERS = 1 UNDERLAY_4 = 'UDP/IPv4' UNDERLAY_6 = 'UDP/IPv6' @@ -148,8 +147,6 @@ def _register_addrs(self, topo_id, as_conf): def _register_srv_entries(self, topo_id, as_conf): srvs = [("control_servers", DEFAULT_CONTROL_SERVERS, "cs")] - if self.args.colibri: - srvs.append(("colibri_servers", DEFAULT_COLIBRI_SERVERS, "co")) for conf_key, def_num, nick in srvs: self._register_srv_entry(topo_id, as_conf, conf_key, def_num, nick) @@ -257,8 +254,6 @@ def _generate_as_topo(self, topo_id, as_conf): def _gen_srv_entries(self, topo_id, as_conf): srvs = [("control_servers", DEFAULT_CONTROL_SERVERS, "cs", "control_service")] srvs.append(("control_servers", DEFAULT_CONTROL_SERVERS, "cs", "discovery_service")) - if self.args.colibri: - srvs.append(("colibri_servers", DEFAULT_COLIBRI_SERVERS, "co", "colibri_service")) for conf_key, def_num, nick, topo_key in srvs: self._gen_srv_entry(topo_id, as_conf, conf_key, def_num, nick, topo_key) @@ -281,8 +276,6 @@ def _gen_srv_entry(self, topo_id, as_conf, conf_key, def_num, nick, def _default_ctrl_port(self, nick): if nick == "cs": return 30252 - if nick == "co": - return 30257 print('Invalid nick: %s' % nick) sys.exit(1)