From 142cc3b7e07d7c827469262923fd63af1d9b7459 Mon Sep 17 00:00:00 2001 From: Amr Elsayyad Date: Thu, 1 Aug 2024 18:29:36 +0300 Subject: [PATCH] feat: add support for selecting the default APN per subscriber (#15453) * feat: add support for selecting the default APN per subscriber Signed-off-by: Amr Elsayyad * chore: add MAGMA_ROOT env var to VSCode settings Signed-off-by: Amr Elsayyad * fix(ci) ci/cd build fails due to unresolved dependancy in unit test Adding `psutil` unit caused automated tests to fail due to unrezloved dependancy in the bazel definition. Signed-off-by: Jordan Vrtanoski --------- Signed-off-by: Amr Elsayyad Signed-off-by: Jordan Vrtanoski Co-authored-by: Jordan Vrtanoski --- .vscode/settings.json | 11 + lte/cloud/go/protos/apn.pb.go | 277 +++++------ .../lte/obsidian/handlers/handlers_test.go | 24 +- .../models/apn_configuration_swaggergen.go | 3 + .../lte/obsidian/models/swagger.v1.yml | 4 + .../m010_default_apns/types/types.go | 7 +- .../protocols/s6a_proxy_servicer.py | 52 ++- .../magma/subscriberdb/store/BUILD.bazel | 2 + .../python/magma/subscriberdb/store/sqlite.py | 441 ++++++++++-------- lte/gateway/python/scripts/subscriber_cli.py | 241 +++++++--- lte/protos/apn.proto | 3 + .../equipment/__tests__/GatewayConfigTest.tsx | 1 + .../views/network/__tests__/NetworkTest.tsx | 2 + .../__tests__/SubscriberAddEditTest.tsx | 2 + nms/app/views/traffic/ApnEdit.tsx | 20 + .../views/traffic/__tests__/ApnAddTest.tsx | 4 + .../traffic/__tests__/TrafficOverviewTest.tsx | 2 + nms/generated/api.ts | 6 + nms/server/util/asyncHandler.ts | 8 +- .../api/v1/go/models/apn_configuration.go | 3 + .../services/obsidian/swagger/v1/swagger.yml | 4 + 21 files changed, 705 insertions(+), 412 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..d840d0867619 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "terminal.integrated.env.linux": { + "MAGMA_ROOT": "${workspaceFolder}" + }, + "terminal.integrated.env.osx": { + "MAGMA_ROOT": "${workspaceFolder}" + }, + "terminal.integrated.env.windows": { + "MAGMA_ROOT": "${workspaceFolder}" + } +} \ No newline at end of file diff --git a/lte/cloud/go/protos/apn.pb.go b/lte/cloud/go/protos/apn.pb.go index 3f5c18e3a957..d88e55785674 100644 --- a/lte/cloud/go/protos/apn.pb.go +++ b/lte/cloud/go/protos/apn.pb.go @@ -479,6 +479,8 @@ type APNConfiguration struct { AssignedStaticIp string `protobuf:"bytes,6,opt,name=assigned_static_ip,json=assignedStaticIp,proto3" json:"assigned_static_ip,omitempty"` // resource is gateway-specific guidance for serving the APN Resource *APNConfiguration_APNResource `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty"` + // is this the default APN? + IsDefault bool `protobuf:"varint,8,opt,name=is_default,json=isDefault,proto3" json:"is_default,omitempty"` } func (x *APNConfiguration) Reset() { @@ -562,6 +564,13 @@ func (x *APNConfiguration) GetResource() *APNConfiguration_APNResource { return nil } +func (x *APNConfiguration) GetIsDefault() bool { + if x != nil { + return x.IsDefault + } + return false +} + // Network wide APN configuration type APNConfig struct { state protoimpl.MessageState @@ -1268,7 +1277,7 @@ var file_lte_protos_apn_proto_rawDesc = []byte{ 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x41, 0x4d, 0x42, 0x52, 0x52, 0x06, 0x62, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x22, 0x25, 0x0a, 0x10, 0x42, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x41, 0x4d, 0x42, 0x52, 0x12, 0x07, 0x0a, 0x03, 0x42, 0x50, 0x53, 0x10, 0x00, 0x12, - 0x08, 0x0a, 0x04, 0x4b, 0x42, 0x50, 0x53, 0x10, 0x01, 0x22, 0x8c, 0x06, 0x0a, 0x10, 0x41, 0x50, + 0x08, 0x0a, 0x04, 0x4b, 0x42, 0x50, 0x53, 0x10, 0x01, 0x22, 0xab, 0x06, 0x0a, 0x10, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x2b, 0x0a, @@ -1293,141 +1302,143 @@ var file_lte_protos_apn_proto_rawDesc = []byte{ 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x50, 0x4e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xbe, 0x01, 0x0a, 0x0a, 0x51, 0x6f, 0x53, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x64, - 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x72, 0x65, 0x65, 0x6d, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x18, - 0x70, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x75, 0x6c, 0x6e, 0x65, - 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, - 0x70, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x81, 0x01, 0x0a, 0x0b, 0x41, 0x50, 0x4e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x6e, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x6e, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, - 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x63, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, - 0x61, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x22, 0x3b, 0x0a, 0x07, 0x50, - 0x44, 0x4e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x34, 0x10, 0x00, - 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x36, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x50, - 0x56, 0x34, 0x56, 0x36, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x4f, - 0x52, 0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, 0x03, 0x22, 0xa6, 0x02, 0x0a, 0x09, 0x41, 0x50, 0x4e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x0b, 0x71, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, - 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x51, 0x6f, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, - 0x0a, 0x71, 0x6f, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x04, 0x61, - 0x6d, 0x62, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x61, 0x67, 0x6d, - 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, - 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x52, 0x04, - 0x61, 0x6d, 0x62, 0x72, 0x12, 0x2e, 0x0a, 0x03, 0x70, 0x64, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, 0x50, - 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x44, 0x4e, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x03, 0x70, 0x64, 0x6e, 0x22, 0x3b, 0x0a, 0x07, 0x50, 0x44, 0x4e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, - 0x36, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x50, 0x56, 0x34, 0x56, 0x36, 0x10, 0x02, 0x12, - 0x10, 0x0a, 0x0c, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x4f, 0x52, 0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, - 0x03, 0x22, 0x81, 0x06, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x61, 0x70, 0x6e, 0x49, 0x64, 0x12, 0x44, 0x0a, - 0x0a, 0x61, 0x70, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x25, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x41, - 0x50, 0x4e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x09, 0x61, 0x70, 0x6e, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x50, 0x72, 0x69, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6e, 0x73, - 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x12, 0x57, 0x0a, 0x11, 0x69, 0x70, 0x5f, - 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x1a, 0xbe, 0x01, 0x0a, 0x0a, 0x51, 0x6f, 0x53, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x64, 0x12, + 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x72, 0x65, 0x65, 0x6d, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x18, 0x70, + 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x70, + 0x72, 0x65, 0x65, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x81, 0x01, 0x0a, 0x0b, 0x41, 0x50, 0x4e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x6e, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x70, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x70, + 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x63, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, 0x61, + 0x63, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x22, 0x3b, 0x0a, 0x07, 0x50, 0x44, + 0x4e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x34, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x36, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x50, 0x56, + 0x34, 0x56, 0x36, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x4f, 0x52, + 0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, 0x03, 0x22, 0xa6, 0x02, 0x0a, 0x09, 0x41, 0x50, 0x4e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x0b, 0x71, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, + 0x6c, 0x74, 0x65, 0x2e, 0x51, 0x6f, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0a, + 0x71, 0x6f, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x04, 0x61, 0x6d, + 0x62, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, + 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x4d, + 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x42, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x52, 0x04, 0x61, + 0x6d, 0x62, 0x72, 0x12, 0x2e, 0x0a, 0x03, 0x70, 0x64, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, 0x50, 0x4e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x44, 0x4e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, + 0x70, 0x64, 0x6e, 0x22, 0x3b, 0x0a, 0x07, 0x50, 0x44, 0x4e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, + 0x0a, 0x04, 0x49, 0x50, 0x56, 0x34, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x50, 0x56, 0x36, + 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x50, 0x56, 0x34, 0x56, 0x36, 0x10, 0x02, 0x12, 0x10, + 0x0a, 0x0c, 0x49, 0x50, 0x56, 0x34, 0x5f, 0x4f, 0x52, 0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, 0x03, + 0x22, 0x81, 0x06, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x61, 0x70, 0x6e, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x0a, + 0x61, 0x70, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x25, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x41, 0x50, + 0x4e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x09, 0x61, 0x70, 0x6e, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6e, 0x73, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x12, 0x57, 0x0a, 0x11, 0x69, 0x70, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x49, 0x70, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0f, 0x69, 0x70, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x70, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x10, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, + 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x25, 0x0a, 0x0a, 0x56, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x1a, 0xe3, 0x01, 0x0a, 0x0f, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x70, 0x12, 0x1f, + 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, 0x61, 0x63, 0x12, + 0x47, 0x0a, 0x0b, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2e, 0x49, 0x70, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x0f, 0x69, 0x70, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x70, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x70, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x56, 0x0a, - 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, - 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x25, 0x0a, 0x0a, 0x56, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x76, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x1a, 0xe3, 0x01, 0x0a, - 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x70, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x70, 0x12, - 0x1f, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4d, 0x61, 0x63, - 0x12, 0x47, 0x0a, 0x0b, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, - 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x65, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x76, 0x6c, 0x61, - 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x56, 0x6c, 0x61, 0x6e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x76, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x22, 0x2f, 0x0a, 0x09, 0x41, 0x50, 0x4e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, - 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, - 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x45, 0x52, 0x4d, 0x49, - 0x54, 0x10, 0x02, 0x22, 0x16, 0x0a, 0x0a, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x08, 0x0a, 0x04, 0x56, 0x4c, 0x41, 0x4e, 0x10, 0x00, 0x22, 0x28, 0x0a, 0x0f, 0x49, - 0x70, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, - 0x0a, 0x07, 0x49, 0x50, 0x5f, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, - 0x48, 0x43, 0x50, 0x10, 0x01, 0x22, 0x52, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, - 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6f, 0x0a, 0x15, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, - 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, - 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x78, 0x0a, 0x1b, 0x4c, 0x69, - 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, - 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x61, - 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, - 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, - 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x32, 0xcf, 0x01, 0x0a, 0x0a, 0x41, 0x70, 0x6e, 0x44, 0x42, 0x43, 0x6c, 0x6f, - 0x75, 0x64, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, - 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x15, 0x4c, 0x69, 0x73, + 0x67, 0x2e, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x65, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x76, 0x6c, 0x61, 0x6e, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, + 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x56, 0x6c, 0x61, 0x6e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x76, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x22, 0x2f, 0x0a, 0x09, 0x41, 0x50, 0x4e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0b, + 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x42, + 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x54, + 0x10, 0x02, 0x22, 0x16, 0x0a, 0x0a, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x08, 0x0a, 0x04, 0x56, 0x4c, 0x41, 0x4e, 0x10, 0x00, 0x22, 0x28, 0x0a, 0x0f, 0x49, 0x70, + 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x49, 0x50, 0x5f, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x48, + 0x43, 0x50, 0x10, 0x01, 0x22, 0x52, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, + 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6f, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x41, + 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, + 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x78, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x61, 0x67, - 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x19, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2f, 0x6c, - 0x74, 0x65, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x4e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, + 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x32, 0xcf, 0x01, 0x0a, 0x0a, 0x41, 0x70, 0x6e, 0x44, 0x42, 0x43, 0x6c, 0x6f, 0x75, + 0x64, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x61, 0x67, 0x6d, + 0x61, 0x2e, 0x6c, 0x74, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x41, 0x70, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x19, 0x6d, 0x61, 0x67, 0x6d, 0x61, 0x2f, 0x6c, 0x74, + 0x65, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/lte/cloud/go/services/lte/obsidian/handlers/handlers_test.go b/lte/cloud/go/services/lte/obsidian/handlers/handlers_test.go index 628bb5cbb294..f0f9d78b631a 100644 --- a/lte/cloud/go/services/lte/obsidian/handlers/handlers_test.go +++ b/lte/cloud/go/services/lte/obsidian/handlers/handlers_test.go @@ -3003,7 +3003,8 @@ func TestListApns(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, - PdnType: 0, + PdnType: 0, + IsDefault: swag.Bool(false), }, }, { @@ -3019,7 +3020,8 @@ func TestListApns(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(5), }, - PdnType: 1, + PdnType: 1, + IsDefault: swag.Bool(false), }, }, }, serdes.Entity) @@ -3046,7 +3048,8 @@ func TestListApns(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, - PdnType: 0, + PdnType: 0, + IsDefault: swag.Bool(false), }, }, "oai.ims": { @@ -3062,7 +3065,8 @@ func TestListApns(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(5), }, - PdnType: 1, + PdnType: 1, + IsDefault: swag.Bool(false), }, }, }), @@ -3104,7 +3108,8 @@ func TestGetApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, - PdnType: 0, + PdnType: 0, + IsDefault: swag.Bool(false), }, }, serdes.Entity) assert.NoError(t, err) @@ -3129,7 +3134,8 @@ func TestGetApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, - PdnType: 0, + PdnType: 0, + IsDefault: swag.Bool(false), }, }, } @@ -3160,6 +3166,7 @@ func TestUpdateApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(5), }, + IsDefault: swag.Bool(false), }, } @@ -3189,6 +3196,7 @@ func TestUpdateApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, + IsDefault: swag.Bool(false), }, }, serdes.Entity) assert.NoError(t, err) @@ -3241,6 +3249,7 @@ func TestDeleteApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(15), }, + IsDefault: swag.Bool(false), }, }, { @@ -3256,6 +3265,7 @@ func TestDeleteApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(5), }, + IsDefault: swag.Bool(false), }, }, }, serdes.Entity) @@ -3289,6 +3299,7 @@ func TestDeleteApn(t *testing.T) { PreemptionVulnerability: swag.Bool(false), PriorityLevel: swag.Uint32(5), }, + IsDefault: swag.Bool(false), }, GraphID: "4", Version: 0, @@ -4408,6 +4419,7 @@ func newAPN(name string) *lteModels.Apn { MaxBandwidthDl: swag.Uint32(100), MaxBandwidthUl: swag.Uint32(100), }, + IsDefault: swag.Bool(false), QosProfile: <eModels.QosProfile{ ClassID: swag.Int32(9), PreemptionCapability: swag.Bool(true), diff --git a/lte/cloud/go/services/lte/obsidian/models/apn_configuration_swaggergen.go b/lte/cloud/go/services/lte/obsidian/models/apn_configuration_swaggergen.go index d7ed141ffaa2..8b3e4a4d79ee 100644 --- a/lte/cloud/go/services/lte/obsidian/models/apn_configuration_swaggergen.go +++ b/lte/cloud/go/services/lte/obsidian/models/apn_configuration_swaggergen.go @@ -24,6 +24,9 @@ type ApnConfiguration struct { // Required: true Ambr *AggregatedMaximumBitrate `json:"ambr"` + // Is this the default APN? + IsDefault *bool `json:"is_default,omitempty"` + // Value identifier for PDN type (0=IPv4 1=IPv6 2=IPv4v6 3=IPv4orv6) // Enum: [0 1 2 3] PdnType uint32 `json:"pdn_type,omitempty"` diff --git a/lte/cloud/go/services/lte/obsidian/models/swagger.v1.yml b/lte/cloud/go/services/lte/obsidian/models/swagger.v1.yml index 3173bdf21a26..21d498454779 100644 --- a/lte/cloud/go/services/lte/obsidian/models/swagger.v1.yml +++ b/lte/cloud/go/services/lte/obsidian/models/swagger.v1.yml @@ -2604,6 +2604,10 @@ definitions: - 2 - 3 description: Value identifier for PDN type (0=IPv4 1=IPv6 2=IPv4v6 3=IPv4orv6) + is_default: + type: boolean + default: false + description: Is this the default APN? apn_name: type: string diff --git a/lte/cloud/go/tools/migrations/m010_default_apns/types/types.go b/lte/cloud/go/tools/migrations/m010_default_apns/types/types.go index 4507d843d13c..c4348cd82f0c 100644 --- a/lte/cloud/go/tools/migrations/m010_default_apns/types/types.go +++ b/lte/cloud/go/tools/migrations/m010_default_apns/types/types.go @@ -32,6 +32,7 @@ const ( defaultQoSPriorityLevel = 15 defaultQoSPreemptionCapability = true defaultQoSPreemptionVulnerability = false + defaultIsDefault = false ) var DefaultAPNVal []byte @@ -47,6 +48,7 @@ var defaultAPN = &ApnConfiguration{ PreemptionVulnerability: swag.Bool(defaultQoSPreemptionVulnerability), PriorityLevel: swag.Uint32(defaultQoSPriorityLevel), }, + IsDefault: swag.Bool(defaultIsDefault), } func init() { @@ -61,6 +63,9 @@ type ApnConfiguration struct { // qos profile // Required: true QosProfile *QosProfile `json:"qos_profile"` + + // is default + IsDefault *bool `json:"is_default,omitempty"` } func (m *ApnConfiguration) MarshalBinary() ([]byte, error) { @@ -91,7 +96,7 @@ func (m *ApnConfiguration) MustMarshalBinary() []byte { } func (m *ApnConfiguration) String() string { - return fmt.Sprintf("{Ambr: %v, QosProfile: %v}", m.Ambr, m.QosProfile) + return fmt.Sprintf("{Ambr: %v, QosProfile: %v, IsDefault: %v}", m.Ambr, m.QosProfile, m.IsDefault) } type AggregatedMaximumBitrate struct { diff --git a/lte/gateway/python/magma/subscriberdb/protocols/s6a_proxy_servicer.py b/lte/gateway/python/magma/subscriberdb/protocols/s6a_proxy_servicer.py index c9104e527a66..a4811db9da4e 100644 --- a/lte/gateway/python/magma/subscriberdb/protocols/s6a_proxy_servicer.py +++ b/lte/gateway/python/magma/subscriberdb/protocols/s6a_proxy_servicer.py @@ -39,6 +39,19 @@ def add_to_server(self, server): s6a_proxy_pb2_grpc.add_S6aProxyServicer_to_server(self, server) def AuthenticationInformation(self, request, context): + """ + Handle the Authentication Information request. + Retrieves user information from the request, performs authentication operations, + increments success/failure metrics, and logs the outcome. + Returns an Authentication Information Answer message. + + Args: + request: AuthenticationInformationRequest message + context: gRPC context + + Returns: + AuthenticationInformationAnswer message + """ print_grpc(request, self._print_grpc_payload, "AIR:") imsi = request.user_name aia = s6a_proxy_pb2.AuthenticationInformationAnswer() @@ -95,6 +108,17 @@ def AuthenticationInformation(self, request, context): print_grpc(aia, self._print_grpc_payload, "AIA:") def UpdateLocation(self, request, context): + """ + Update the location of a subscriber based on the request information. + + Args: + self: The object instance. + request: The request object containing user information. + context: The context of the request. + + Returns: + s6a_proxy_pb2.UpdateLocationAnswer: The answer containing the updated location information. + """ print_grpc(request, self._print_grpc_payload, "ULR:") imsi = request.user_name ula = s6a_proxy_pb2.UpdateLocationAnswer() @@ -124,6 +148,8 @@ def UpdateLocation(self, request, context): for apn in sub_data.non_3gpp.apn_config: sec_apn = ula.apn.add() sec_apn.context_id = context_id + if apn.is_default: + ula.default_context_id = context_id context_id += 1 sec_apn.service_selection = apn.service_selection sec_apn.qos_profile.class_id = apn.qos_profile.class_id @@ -151,6 +177,17 @@ def UpdateLocation(self, request, context): return ula def PurgeUE(self, request, context): + """ + Handle a PurgeUE request. + + Args: + self: The class instance. + request: The request object containing information about the PurgeUE request. + context: The context of the RPC call. + + Returns: + s6a_proxy_pb2.PurgeUEAnswer: The response containing the result of the PurgeUE operation. + """ logging.warning( "Purge request not implemented: %s %s", request.DESCRIPTOR.full_name, MessageToJson(request), @@ -159,10 +196,17 @@ def PurgeUE(self, request, context): print_grpc(pur, self._print_grpc_payload, "PUR:") return pur - @staticmethod - def encode_msisdn(msisdn: str) -> bytes: - # Mimic how the MSISDN is encoded in ULA : 3GPP TS 29.329-f10 - # For odd length MSISDN pad it with an extra 'F'/'1111' + def encode_msisdn(self, msisdn: str) -> bytes: + """ + Mimic how the MSISDN is encoded in ULA : 3GPP TS 29.329-f10 + For odd length MSISDN pad it with an extra 'F'/'1111' + + Args: + msisdn: The MSISDN to encode. + + Returns: + bytes: The encoded MSISDN. + """ if len(msisdn) % 2 != 0: msisdn = msisdn + "F" result = [] diff --git a/lte/gateway/python/magma/subscriberdb/store/BUILD.bazel b/lte/gateway/python/magma/subscriberdb/store/BUILD.bazel index f94cb1b57839..be3b15c68fc7 100644 --- a/lte/gateway/python/magma/subscriberdb/store/BUILD.bazel +++ b/lte/gateway/python/magma/subscriberdb/store/BUILD.bazel @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@python_deps//:requirements.bzl", "requirement") load("@rules_python//python:defs.bzl", "py_library") py_library( @@ -19,6 +20,7 @@ py_library( ":base", ":onready", "//lte/gateway/python/magma/subscriberdb:sid", + requirement("psutil"), ], ) diff --git a/lte/gateway/python/magma/subscriberdb/store/sqlite.py b/lte/gateway/python/magma/subscriberdb/store/sqlite.py index 2b943fc6003a..90711a67dd40 100644 --- a/lte/gateway/python/magma/subscriberdb/store/sqlite.py +++ b/lte/gateway/python/magma/subscriberdb/store/sqlite.py @@ -13,12 +13,12 @@ import logging import sqlite3 -import subprocess from collections import defaultdict from contextlib import contextmanager from datetime import datetime from typing import List, NamedTuple +import psutil from lte.protos.subscriberdb_pb2 import SubscriberData from magma.subscriberdb.sid import SIDUtils from magma.subscriberdb.store.base import ( @@ -32,6 +32,13 @@ class DigestDBInfo(NamedTuple): + """ + Named tuple representing the locations of the root and leaf digest databases. + + Attributes: + root_digest_db_location (str): The location of the root digest database. + leaf_digests_db_location (str): The location of the leaf digests database. + """ root_digest_db_location: str leaf_digests_db_location: str @@ -103,35 +110,23 @@ def _create_store(self) -> None: already. """ for db_location in self._db_locations: - conn = sqlite3.connect(db_location, uri=True) - try: - with conn: - conn.execute( - "CREATE TABLE IF NOT EXISTS subscriberdb" - "(subscriber_id text PRIMARY KEY, data text)", - ) - finally: - conn.close() - - conn = sqlite3.connect(self._root_digest_db_location, uri=True) - try: - with conn: + with sqlite3.connect(db_location, uri=True) as conn: conn.execute( - "CREATE TABLE IF NOT EXISTS subscriber_root_digest" - "(digest string PRIMARY KEY, updated_at timestamp)", + "CREATE TABLE IF NOT EXISTS subscriberdb" + "(subscriber_id text PRIMARY KEY, data text)", ) - finally: - conn.close() - conn = sqlite3.connect(self._leaf_digests_db_location, uri=True) - try: - with conn: - conn.execute( - "CREATE TABLE IF NOT EXISTS subscriber_leaf_digests" - "(sid string PRIMARY KEY, digest string)", - ) - finally: - conn.close() + with sqlite3.connect(self._root_digest_db_location, uri=True) as conn: + conn.execute( + "CREATE TABLE IF NOT EXISTS subscriber_root_digest" + "(digest string PRIMARY KEY, updated_at timestamp)", + ) + + with sqlite3.connect(self._leaf_digests_db_location, uri=True) as conn: + conn.execute( + "CREATE TABLE IF NOT EXISTS subscriber_leaf_digests" + "(sid string PRIMARY KEY, digest string)", + ) def add_subscriber(self, subscriber_data: SubscriberData): """ @@ -140,22 +135,19 @@ def add_subscriber(self, subscriber_data: SubscriberData): sid = SIDUtils.to_str(subscriber_data.sid) data_str = subscriber_data.SerializeToString() db_location = self._db_locations[self._sid2bucket(sid)] - conn = sqlite3.connect(db_location, uri=True) - try: - with conn: - res = conn.execute( - "SELECT data FROM subscriberdb WHERE " - "subscriber_id = ?", (sid,), - ) - if res.fetchone(): - raise DuplicateSubscriberError(sid) + with sqlite3.connect(db_location, uri=True) as conn: + res = conn.execute( + "SELECT data FROM subscriberdb WHERE " + "subscriber_id = ?", (sid,), + ) + if res.fetchone(): + raise DuplicateSubscriberError(sid) + + conn.execute( + "INSERT INTO subscriberdb(subscriber_id, data) " + "VALUES (?, ?)", (sid, data_str), + ) - conn.execute( - "INSERT INTO subscriberdb(subscriber_id, data) " - "VALUES (?, ?)", (sid, data_str), - ) - finally: - conn.close() self._on_ready.add_subscriber(subscriber_data) @contextmanager @@ -190,30 +182,30 @@ def upsert_subscriber(self, subscriber_data: SubscriberData) -> None: """ Check if the given subscriber exists in store. If so, update subscriber data; otherwise, add subscriber. + + Args: + subscriber_data: the data of the subscriber to be upserted. """ sid = SIDUtils.to_str(subscriber_data.sid) data_str = subscriber_data.SerializeToString() db_location = self._db_locations[self._sid2bucket(sid)] - conn = sqlite3.connect(db_location, uri=True) - try: - with conn: - res = conn.execute( - "SELECT subscriber_id FROM subscriberdb WHERE " - "subscriber_id = ?", (sid,), + with sqlite3.connect(db_location, uri=True) as conn: + res = conn.execute( + "SELECT subscriber_id FROM subscriberdb WHERE " + "subscriber_id = ?", (sid,), + ) + row = res.fetchone() + if row is None: + conn.execute( + "INSERT INTO subscriberdb(subscriber_id, data) " + "VALUES (?, ?)", (sid, data_str), ) - row = res.fetchone() - if row is None: - conn.execute( - "INSERT INTO subscriberdb(subscriber_id, data) " - "VALUES (?, ?)", (sid, data_str), - ) - else: - conn.execute( - "UPDATE subscriberdb SET data = ? " - "WHERE subscriber_id = ?", (data_str, sid), - ) - finally: - conn.close() + else: + conn.execute( + "UPDATE subscriberdb SET data = ? " + "WHERE subscriber_id = ?", (data_str, sid), + ) + self._on_ready.upsert_subscriber(subscriber_data) def delete_subscriber(self, subscriber_id) -> None: @@ -225,18 +217,18 @@ def delete_subscriber(self, subscriber_id) -> None: and changes the leaf digests. However, because deleting a subscriber only happens during testing or debugging, it's easier to just blow away all digest data. + + Args: + subscriber_id: The subscriber ID to delete """ db_location = self._db_locations[self._sid2bucket(subscriber_id)] - conn = sqlite3.connect(db_location, uri=True) - try: + with sqlite3.connect(db_location, uri=True) as conn: self.clear_digests() - with conn: - conn.execute( - "DELETE FROM subscriberdb WHERE subscriber_id = ?", - (subscriber_id,), - ) - finally: - conn.close() + conn.execute( + "DELETE FROM subscriberdb WHERE subscriber_id = ?", + (subscriber_id,), + ) + self._on_ready.delete_subscriber(subscriber_id) def delete_all_subscribers(self): @@ -244,22 +236,17 @@ def delete_all_subscribers(self): Remove all the subscribers from the store """ for db_location in self._db_locations: - conn = sqlite3.connect(db_location, uri=True) - try: - self.clear_digests() - with conn: - conn.execute("DELETE FROM subscriberdb") - finally: - conn.close() + self.clear_digests() + with sqlite3.connect(db_location, uri=True) as conn: + conn.execute("DELETE FROM subscriberdb") def get_subscriber_data(self, subscriber_id): """ Return the auth key for the subscriber. """ db_location = self._db_locations[self._sid2bucket(subscriber_id)] - conn = sqlite3.connect(db_location, uri=True) try: - with conn: + with sqlite3.connect(db_location, uri=True) as conn: res = conn.execute( "SELECT data FROM subscriberdb WHERE " "subscriber_id = ?", (subscriber_id,), @@ -267,77 +254,83 @@ def get_subscriber_data(self, subscriber_id): row = res.fetchone() if not row: raise SubscriberNotFoundError(subscriber_id) - except sqlite3.OperationalError: - # Print the process holding the lock - db_parts = db_location.split(":", 1) - if (len(db_parts) == 2) and db_parts[1]: - path_str = db_parts[1].split("?") - output = subprocess.Popen( - ["/usr/bin/fuser", "-uv", path_str[0]], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - ) - logging.info(output.communicate()) - raise SubscriberServerTooBusy(subscriber_id) - finally: - conn.close() + except sqlite3.OperationalError as exc: + self._log_db_lock_info(db_location) + raise SubscriberServerTooBusy(subscriber_id) from exc + subscriber_data = SubscriberData() subscriber_data.ParseFromString(row[0]) return subscriber_data + def _log_db_lock_info(self, db_location): + """ + Log information about processes holding locks on the database file. + """ + db_parts = db_location.split(":", 1) + if len(db_parts) != 2 or not db_parts[1]: + return + + path_str = db_parts[1].split("?")[0] + for proc in psutil.process_iter(['pid', 'name', 'open_files']): + try: + for file in proc.open_files(): + if file.path == path_str: + logging.info("Process holding lock: PID=%d, Name=%s", proc.pid, proc.name()) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + logging.warning("Unable to access process information") + def list_subscribers(self): """ Return the list of subscribers stored """ sub_list = [] for db_location in self._db_locations: - conn = sqlite3.connect(db_location, uri=True) - try: - with conn: - res = conn.execute( - "SELECT subscriber_id FROM subscriberdb", - ) - sub_list.extend([row[0] for row in res]) - finally: - conn.close() + with sqlite3.connect(db_location, uri=True) as conn: + res = conn.execute( + "SELECT subscriber_id FROM subscriberdb", + ) + sub_list.extend([row[0] for row in res]) + return sub_list def update_subscriber(self, subscriber_data): """ - Method that updates the subscriber. edit_subscriber should - be generally used since that guarantees the read/update/write - atomicity, but this can be used if the application can - guarantee the atomicity using a lock. + Update the subscriber. edit_subscriber should be generally used since that guarantees + the read/update/write atomicity, but this can be used if the application can guarantee + the atomicity using a lock. Args: - subscriber_data - SubscriberData protobuf message - Raises: - SubscriberNotFoundError if the subscriber is not present + subscriber_data: SubscriberData protobuf message + Raises: + SubscriberNotFoundError: If the subscriber is not present """ sid = SIDUtils.to_str(subscriber_data.sid) data_str = subscriber_data.SerializeToString() db_location = self._db_locations[self._sid2bucket(sid)] - conn = sqlite3.connect(db_location, uri=True) - - try: - with conn: - res = conn.execute( - "UPDATE subscriberdb SET data = ? " - "WHERE subscriber_id = ?", (data_str, sid), - ) - if not res.rowcount: - raise SubscriberNotFoundError(sid) - finally: - conn.close() + with sqlite3.connect(db_location, uri=True) as conn: + res = conn.execute( + "UPDATE subscriberdb SET data = ? " + "WHERE subscriber_id = ?", (data_str, sid), + ) + if not res.rowcount: + raise SubscriberNotFoundError(sid) def resync(self, subscribers): """ - Method that should resync the store with the mentioned list of - subscribers. The resync leaves the current state of subscribers - intact. + Resync the store with the mentioned list of subscribers. + + This method takes a list of `SubscriberData` objects and resynchronizes the store with the + provided subscribers. It first groups the subscribers by their bucket using the + `_sid2bucket` method. Then, for each bucket, it connects to the corresponding database, + captures the current state of the subscribers, clears the subscriber table, and adds the + subscribers back with their current state. Args: - subscribers - list of subscribers to be in the store. + subscribers: list of subscribers to be in the store. + + Raises: + SubscriberNotFoundError: If a subscriber is not found in the current state. """ bucket_subs = defaultdict(list) for sub in subscribers: @@ -345,52 +338,51 @@ def resync(self, subscribers): bucket_subs[self._sid2bucket(sid)].append(sub) for i, db_location in enumerate(self._db_locations): - conn = sqlite3.connect(db_location, uri=True) - try: - with conn: - # Capture the current state of the subscribers - res = conn.execute( - "SELECT subscriber_id, data FROM subscriberdb", + with sqlite3.connect(db_location, uri=True) as conn: + # Capture the current state of the subscribers + res = conn.execute( + "SELECT subscriber_id, data FROM subscriberdb", + ) + current_state = { + row[0]: SubscriberData().ParseFromString(row[1]).state + for row in res + } + + # Clear all subscribers + conn.execute("DELETE FROM subscriberdb") + + # Add the subscribers with the current state + for sub in bucket_subs[i]: + sid = SIDUtils.to_str(sub.sid) + if sid in current_state: + sub.state.CopyFrom(current_state.get(sid)) + data_str = sub.SerializeToString() + conn.execute( + "INSERT INTO subscriberdb(subscriber_id, data) " + "VALUES (?, ?)", (sid, data_str), ) - current_state = {} - for row in res: - sub = SubscriberData() - sub.ParseFromString(row[1]) - current_state[row[0]] = sub.state - - # Clear all subscribers - conn.execute("DELETE FROM subscriberdb") - - # Add the subscribers with the current state - for sub in bucket_subs[i]: - sid = SIDUtils.to_str(sub.sid) - if sid in current_state: - sub.state.CopyFrom(current_state[sid]) - data_str = sub.SerializeToString() - conn.execute( - "INSERT INTO subscriberdb(subscriber_id, data) " - "VALUES (?, ?)", (sid, data_str), - ) - finally: - conn.close() + self._on_ready.resync(subscribers) def get_current_root_digest(self) -> str: """ - Return the current subscriber digest stored in the db. + Retrieve the current root digest from the subscriber database. + + Returns: + A string containing the current root digest. + + Description: + This function connects to the root digest database, retrieves the latest digest + and returns it. """ - conn = sqlite3.connect(self._root_digest_db_location, uri=True) - try: - with conn: - res = conn.execute( - "SELECT digest, updated_at FROM subscriber_root_digest " - "ORDER BY updated_at DESC", - ) - row = res.fetchone() - if not row: - row = ["", None] - finally: - conn.close() + with sqlite3.connect(self._root_digest_db_location, uri=True) as conn: + res = conn.execute( + "SELECT digest, updated_at FROM subscriber_root_digest " + "ORDER BY updated_at DESC", + ) + row = res.fetchone() + if not row: + row = ["", None] digest = str(row[0]) logging.info("get digest stored in gateway: %s", digest) @@ -398,78 +390,110 @@ def get_current_root_digest(self) -> str: def update_root_digest(self, new_digest: str) -> None: """ - Replace the old digest in the db with the new digest. + Update the root digest in the subscriber database with the new digest provided. + + Args: + new_digest (str): The new digest to update. + + Description: + This function connects to the root digest database, deletes the existing digest, and + inserts the new one along with the current datetime, then calls the `update_root_digest` + method of the `_on_digests_ready` object with the new digest. + + Note: + - The function assumes that the gRPC client and command-line arguments are valid. + - The function does not perform any input validation. + - The function does not handle any exceptions. """ - conn = sqlite3.connect(self._root_digest_db_location, uri=True) - try: - with conn: - conn.execute("DELETE FROM subscriber_root_digest") + with sqlite3.connect(self._root_digest_db_location, uri=True) as conn: + conn.execute("DELETE FROM subscriber_root_digest") - conn.execute( - "INSERT INTO subscriber_root_digest(digest, updated_at) " - "VALUES (?, ?)", (new_digest, datetime.now()), - ) - finally: - conn.close() + conn.execute( + "INSERT INTO subscriber_root_digest(digest, updated_at) " + "VALUES (?, ?)", (new_digest, datetime.now()), + ) logging.info("update root digest stored in gateway: %s", new_digest) self._on_digests_ready.update_root_digest(new_digest) def get_current_leaf_digests(self) -> List[LeafDigest]: + """ + Retrieve the current leaf digests from the subscriber leaf digests database. + + Returns: + A list of LeafDigest objects containing the current leaf digests. + """ digests = [] - conn = sqlite3.connect(self._leaf_digests_db_location, uri=True) - try: - with conn: - res = conn.execute( - "SELECT sid, digest FROM subscriber_leaf_digests ", - ) + with sqlite3.connect(self._leaf_digests_db_location, uri=True) as conn: + res = conn.execute( + "SELECT sid, digest FROM subscriber_leaf_digests ", + ) - for row in res: - digest = LeafDigest( - id=row[0], - digest=Digest(md5_base64_digest=row[1]), - ) - digests.append(digest) - finally: - conn.close() + for row in res: + digest = LeafDigest( + id=row[0], + digest=Digest(md5_base64_digest=row[1]), + ) + digests.append(digest) return digests def update_leaf_digests(self, new_digests: List[LeafDigest]) -> None: - conn = sqlite3.connect(self._leaf_digests_db_location, uri=True) - try: - with conn: + """ + Update the leaf digests in the subscriber database with the new digests provided. + + Args: + new_digests: A list of LeafDigest objects containing the new digests to update. + """ + with sqlite3.connect(self._leaf_digests_db_location, uri=True) as conn: + conn.execute( + "DELETE FROM subscriber_leaf_digests", + ) + for leaf_digest in new_digests: + sid = leaf_digest.id + digest = leaf_digest.digest.md5_base64_digest conn.execute( - "DELETE FROM subscriber_leaf_digests", + "INSERT INTO subscriber_leaf_digests(sid, digest)" + "VALUES (?, ?)", (sid, digest), ) - for leaf_digest in new_digests: - sid = leaf_digest.id - digest = leaf_digest.digest.md5_base64_digest - conn.execute( - "INSERT INTO subscriber_leaf_digests(sid, digest)" - "VALUES (?, ?)", (sid, digest), - ) - finally: - conn.close() + self._on_digests_ready.update_leaf_digests(new_digests) def clear_digests(self): """ - Clear all subscriber digest info. + Clear the digests stored in the database by updating the root digest to an empty string and + updating the leaf digests to an empty list. """ self.update_root_digest("") self.update_leaf_digests([]) async def on_ready(self): + """ + Wait asynchronously for the `_on_ready` event to be set. + + Returns: + Awaitable[None]: An awaitable that resolves when the `_on_ready` event is set. + """ return await self._on_ready.event.wait() async def on_digests_ready(self): + """ + Wait asynchronously for the `_on_digests_ready` event to be set. + + Returns: + Awaitable[None]: An awaitable that resolves when the `_on_digests_ready` event is set. + """ return await self._on_digests_ready.event.wait() def _update_apn(self, apn_config, apn_data): """ - Method that populates apn data. + Update the APN configuration based on the provided APN data. + + Args: + apn_config: The APN configuration to be updated. + apn_data: The APN data containing the new values. """ + apn_config.is_default = apn_data.is_default apn_config.service_selection = apn_data.service_selection apn_config.qos_profile.class_id = apn_data.qos_profile.class_id apn_config.qos_profile.priority_level = ( @@ -486,7 +510,14 @@ def _update_apn(self, apn_config, apn_data): def _sid2bucket(self, subscriber_id): """ - Maps Subscriber ID to bucket + Calculate the bucket number based on the last `self._sid_digits` digits of the `subscriber_id`. + + Args: + subscriber_id (str): The ID of the subscriber. + + Returns: + int: The bucket number corresponding to the last `self._sid_digits` digits of the `subscriber_id`. + If the conversion to an integer fails, a default value of 0 is returned. """ try: bucket = int(subscriber_id[-self._sid_digits:]) diff --git a/lte/gateway/python/scripts/subscriber_cli.py b/lte/gateway/python/scripts/subscriber_cli.py index 980ca3be4756..b50ba44b1a85 100755 --- a/lte/gateway/python/scripts/subscriber_cli.py +++ b/lte/gateway/python/scripts/subscriber_cli.py @@ -31,12 +31,57 @@ @grpc_wrapper def add_subscriber(client, args): + """ + Add a subscriber to the SubscriberDB service. + + Args: + client (SubscriberDBStub): The gRPC client for the SubscriberDB service. + args (argparse.Namespace): The command line arguments. + + Returns: + None + + Raises: + None + + Description: + This function adds a subscriber to the SubscriberDB service. It takes a gRPC + client and command-line arguments as input. The function initializes the GSM, + LTE, state, and sub_network objects. + + If the --gsm_auth_tuple flag is provided, the function sets the GSM state to + ACTIVE and appends the provided authentication tuples to the gsm.auth_tuples + list. + + If the --lte_auth_key flag is provided, the function sets the LTE state to + ACTIVE and sets the LTE authentication key. + + If the --lte_auth_next_seq flag is provided, the function sets the LTE next + sequence number. + + If the --lte_auth_opc flag is provided, the function sets the LTE authentication + OPC. + + If the --forbidden_network_types flag is provided, the function checks if the + number of forbidden network types is greater than 2 and prints an error message. + If the provided network types are valid, the function adds them to the + sub_network.forbidden_network_types list. + + Finally, the function creates a SubscriberData object with the provided SID, + GSM subscription details, LTE subscription details, subscriber state, and + forbidden network types, and calls the AddSubscriber RPC on the client. + + Note: + - The function assumes that the gRPC client and command-line arguments are valid. + - The function does not perform any input validation. + - The function does not handle any exceptions. + """ gsm = GSMSubscription() lte = LTESubscription() state = SubscriberState() sub_network = CoreNetworkType() - if len(args.gsm_auth_tuple) != 0: + if args.gsm_auth_tuple: gsm.state = GSMSubscription.ACTIVE for auth_tuple in args.gsm_auth_tuple: gsm.auth_tuples.append(bytes.fromhex(auth_tuple)) @@ -72,33 +117,81 @@ def add_subscriber(client, args): @grpc_wrapper def update_subscriber(client, args): + """ + Update a subscriber's information. + + Args: + client (SubscriberDBStub): The gRPC client for the SubscriberDB service. + args (Namespace): The parsed command-line arguments. + + Returns: + None + + Raises: + None + + Description: + This function updates a subscriber's information in the SubscriberDB service. + It takes a gRPC client and command-line arguments as input. The function creates + a SubscriberUpdate object and populates its data field with the subscriber's + information. It then updates the fields parameter with the paths of the fields + that need to be updated. + + If the gsm_auth_tuple argument is provided, the function sets the gsm.state field + to ACTIVE and appends the provided authentication tuples to the gsm.auth_tuples + field. It also appends the 'gsm.state' and 'gsm.auth_tuples' paths to the fields + parameter. + + If the lte_auth_key argument is provided, the function sets the lte.state field + to ACTIVE and sets the lte.auth_key field to the provided authentication key. It + also appends the 'lte.state' and 'lte.auth_key' paths to the fields parameter. + + If the lte_auth_next_seq argument is provided, the function sets the + state.lte_auth_next_seq field to the provided value. It appends the + 'state.lte_auth_next_seq' path to the fields parameter. + + If the lte_auth_opc argument is provided, the function sets the lte.state field + to ACTIVE and sets the lte.auth_opc field to the provided authentication OPC. It + also appends the 'lte.state' and 'lte.auth_opc' paths to the fields parameter. + + If the apn_config argument is provided, the function creates a dictionary of + APN configuration parameters and populates the non_3gpp.apn_config field of the + data object with the provided APN configurations. It appends the 'non_3gpp' path + to the fields parameter. + + Finally, the function calls the UpdateSubscriber method of the gRPC client with + the SubscriberUpdate object. + + Note: + - The function assumes that the gRPC client and command-line arguments are valid. + - The function does not perform any input validation. + - The function does not handle any exceptions. + """ update = SubscriberUpdate() - update.data.sid.CopyFrom(SIDUtils.to_pb(args.sid)) - gsm = update.data.gsm - lte = update.data.lte - non_3gpp = update.data.non_3gpp + data = update.data + data.sid.CopyFrom(SIDUtils.to_pb(args.sid)) fields = update.mask.paths - if len(args.gsm_auth_tuple) != 0: - gsm.state = GSMSubscription.ACTIVE + if args.gsm_auth_tuple: + data.gsm.state = GSMSubscription.ACTIVE for auth_tuple in args.gsm_auth_tuple: - gsm.auth_tuples.append(bytes.fromhex(auth_tuple)) + data.gsm.auth_tuples.append(bytes.fromhex(auth_tuple)) fields.append('gsm.state') fields.append('gsm.auth_tuples') if args.lte_auth_key is not None: - lte.state = LTESubscription.ACTIVE - lte.auth_key = bytes.fromhex(args.lte_auth_key) + data.lte.state = LTESubscription.ACTIVE + data.lte.auth_key = bytes.fromhex(args.lte_auth_key) fields.append('lte.state') fields.append('lte.auth_key') if args.lte_auth_next_seq is not None: - update.data.state.lte_auth_next_seq = args.lte_auth_next_seq + data.state.lte_auth_next_seq = args.lte_auth_next_seq fields.append('state.lte_auth_next_seq') if args.lte_auth_opc is not None: - lte.state = LTESubscription.ACTIVE - lte.auth_opc = bytes.fromhex(args.lte_auth_opc) + data.lte.state = LTESubscription.ACTIVE + data.lte.auth_opc = bytes.fromhex(args.lte_auth_opc) fields.append('lte.state') fields.append('lte.auth_opc') @@ -115,6 +208,7 @@ def update_subscriber(client, args): vlan_id = "vlan" gw_ip = "gw_ip" gw_mac = "gw_mac" + is_default = "is_default" apn_keys = ( apn_name, @@ -129,18 +223,19 @@ def update_subscriber(client, args): vlan_id, gw_ip, gw_mac, + is_default, ) apn_data = args.apn_config for apn_d in apn_data: apn_val = apn_d.split(",") - if len(apn_val) != 12: + if len(apn_val) != 13: print( "Incorrect APN parameters." "Please check: subscriber_cli.py update -h", ) return apn_dict = dict(zip(apn_keys, apn_val)) - apn_config = non_3gpp.apn_config.add() + apn_config = data.non_3gpp.apn_config.add() apn_config.service_selection = apn_dict[apn_name] apn_config.qos_profile.class_id = int(apn_dict[qci]) apn_config.qos_profile.priority_level = int(apn_dict[priority]) @@ -154,6 +249,7 @@ def update_subscriber(client, args): apn_config.ambr.max_bandwidth_dl = int(apn_dict[dl]) apn_config.pdn = int(apn_dict[pdn_type]) apn_config.assigned_static_ip = apn_dict[static_ip] + apn_config.is_default = apn_dict[is_default] if apn_dict[vlan_id]: apn_config.resource.vlan_id = int(apn_dict[vlan_id]) @@ -170,24 +266,41 @@ def update_subscriber(client, args): @grpc_wrapper def delete_subscriber(client, args): + """ + Delete a subscriber using the provided client and arguments. + + Args: + client (SubscriberDBServicer): The gRPC client for the SubscriberDBServicer. + args: The command line arguments passed to the script. + """ client.DeleteSubscriber(SIDUtils.to_pb(args.sid)) @grpc_wrapper def get_subscriber(client, args): + """ + Fetch subscriber data based on the provided client and arguments. + """ data = client.GetSubscriberData(SIDUtils.to_pb(args.sid)) print(data) @grpc_wrapper def list_subscribers(client, args): + """ + List all subscribers by making a gRPC request to the client. + + Args: + client (SubscriberLookupServicer): The gRPC client for the SubscriberLookupServicer. + args: The command line arguments passed to the script. + """ for sid in client.ListSubscribers(Void()).sids: print(SIDUtils.to_str(sid)) def create_parser(): """ - Creates the argparse parser with all the arguments. + Create the argparse parser with all the arguments. """ parser = argparse.ArgumentParser( description='Management CLI for SubscriberDB', @@ -203,58 +316,59 @@ def create_parser(): parser_list = subparsers.add_parser("list", help="List all subscriber ids") # Add arguments - for cmd in [ + for cmd in list( parser_add, parser_del, parser_update, parser_get, - ]: + ): cmd.add_argument("sid", help="Subscriber identifier") - for cmd in [parser_add]: - cmd.add_argument( - "--gsm-auth-tuple", - default=[], - action="append", - help="GSM authentication tuple (hex digits)", - ) - cmd.add_argument("--lte-auth-key", help="LTE authentication key") - cmd.add_argument("--lte-auth-opc", help="LTE authentication opc") - cmd.add_argument( - "--lte-auth-next-seq", - type=int, - help="LTE authentication seq number (hex digits)", - ) - cmd.add_argument("--forbidden-network-types", help="Core NetworkType Restriction") - - for cmd in [parser_update]: - cmd.add_argument( - "--gsm-auth-tuple", - default=[], - action="append", - help="GSM authentication tuple (hex digits)", - ) - cmd.add_argument("--lte-auth-key", help="LTE authentication key") - cmd.add_argument("--lte-auth-opc", help="LTE authentication opc") - cmd.add_argument( - "--lte-auth-next-seq", - type=int, - help="LTE authentication seq number (hex digits)", - ) - cmd.add_argument( - "--apn-config", - action="append", - help="APN parameters to add/update in the order :" - " [apn-name, qci, priority, preemption-capability," - " preemption-vulnerability, mbr-ul, mbr-dl, pdn-type," - " [0-IPv4, 1-IPv6, 2-IPv4v6]" - " static-ip, vlan_id, internet_gw_ip, internet_gw_mac]" - " [e.g --apn-config ims,5,15,1,1,1000,2000,1,,,," - " --apn-config internet,9,1,0,0,3000,4000,0,1.2.3.4,,," - " --apn-config internet,9,1,0,0,3000,4000,2," - "1.2.3.4,1,2.2.2.2,11:22:33:44:55:66]", - ) -# Add function callbacks + # Add subcommand arguments + parser_add.add_argument( + "--gsm-auth-tuple", + default=[], + action="append", + help="GSM authentication tuple (hex digits)", + ) + parser_add.add_argument("--lte-auth-key", help="LTE authentication key") + parser_add.add_argument("--lte-auth-opc", help="LTE authentication opc") + parser_add.add_argument( + "--lte-auth-next-seq", + type=int, + help="LTE authentication seq number (hex digits)", + ) + parser_add.add_argument("--forbidden-network-types", help="Core NetworkType Restriction") + + # Update subcommand arguments + parser_update.add_argument( + "--gsm-auth-tuple", + default=[], + action="append", + help="GSM authentication tuple (hex digits)", + ) + parser_update.add_argument("--lte-auth-key", help="LTE authentication key") + parser_update.add_argument("--lte-auth-opc", help="LTE authentication opc") + parser_update.add_argument( + "--lte-auth-next-seq", + type=int, + help="LTE authentication seq number (hex digits)", + ) + parser_update.add_argument( + "--apn-config", + action="append", + help="APN parameters to add/update in the order :" + " [apn-name, qci, priority, preemption-capability," + " preemption-vulnerability, mbr-ul, mbr-dl, pdn-type," + " [0-IPv4, 1-IPv6, 2-IPv4v6]" + " static-ip, vlan_id, internet_gw_ip, internet_gw_mac]" + " [e.g --apn-config ims,5,15,1,1,1000,2000,1,,,," + " --apn-config internet,9,1,0,0,3000,4000,0,1.2.3.4,,," + " --apn-config internet,9,1,0,0,3000,4000,2," + "1.2.3.4,1,2.2.2.2,11:22:33:44:55:66]", + ) + + # Add function callbacks parser_add.set_defaults(func=add_subscriber) parser_del.set_defaults(func=delete_subscriber) parser_update.set_defaults(func=update_subscriber) @@ -264,6 +378,9 @@ def create_parser(): def main(): + """ + Create a parser, parses arguments, check for a command, and execute a subcommand function. + """ parser = create_parser() # Parse the args diff --git a/lte/protos/apn.proto b/lte/protos/apn.proto index 52a72d336c8b..5c636d8da57a 100644 --- a/lte/protos/apn.proto +++ b/lte/protos/apn.proto @@ -73,6 +73,9 @@ message APNConfiguration { } // resource is gateway-specific guidance for serving the APN APNResource resource = 7; + + // is this the default APN? + bool is_default = 8; } // APN MODELS 2.0 diff --git a/nms/app/views/equipment/__tests__/GatewayConfigTest.tsx b/nms/app/views/equipment/__tests__/GatewayConfigTest.tsx index 1c1be2757979..ef169b108954 100644 --- a/nms/app/views/equipment/__tests__/GatewayConfigTest.tsx +++ b/nms/app/views/equipment/__tests__/GatewayConfigTest.tsx @@ -172,6 +172,7 @@ const mockApns: Record = { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'oai.ipv4', }, diff --git a/nms/app/views/network/__tests__/NetworkTest.tsx b/nms/app/views/network/__tests__/NetworkTest.tsx index 8069209bfd7c..4d2ca30af9f8 100644 --- a/nms/app/views/network/__tests__/NetworkTest.tsx +++ b/nms/app/views/network/__tests__/NetworkTest.tsx @@ -232,6 +232,7 @@ describe('', () => { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'internet', }, @@ -247,6 +248,7 @@ describe('', () => { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'oai.ipv4', }, diff --git a/nms/app/views/subscriber/__tests__/SubscriberAddEditTest.tsx b/nms/app/views/subscriber/__tests__/SubscriberAddEditTest.tsx index b9658e5e70d5..cfb2a3a75877 100644 --- a/nms/app/views/subscriber/__tests__/SubscriberAddEditTest.tsx +++ b/nms/app/views/subscriber/__tests__/SubscriberAddEditTest.tsx @@ -137,6 +137,7 @@ const apns = { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'apn_0', }, @@ -152,6 +153,7 @@ const apns = { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'apn_1', }, diff --git a/nms/app/views/traffic/ApnEdit.tsx b/nms/app/views/traffic/ApnEdit.tsx index 685706eb170a..7ab363a8b391 100644 --- a/nms/app/views/traffic/ApnEdit.tsx +++ b/nms/app/views/traffic/ApnEdit.tsx @@ -29,6 +29,7 @@ import Select from '@mui/material/Select'; import Switch from '@mui/material/Switch'; import Text from '../../theme/design-system/Text'; import {AltFormField, AltFormFieldSubheading} from '../../components/FormField'; +import {Checkbox, FormControlLabel} from '@mui/material'; import {getErrorMessage} from '../../util/ErrorUtils'; import {useContext, useEffect, useState} from 'react'; import {useEnqueueSnackbar} from '../../hooks/useSnackbar'; @@ -46,6 +47,7 @@ const DEFAULT_APN_CONFIG = { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, pdn_type: 0, }, apn_name: '', @@ -119,6 +121,10 @@ export function ApnEdit(props: Props) { props.apn?.apn_configuration?.pdn_type || DEFAULT_APN_CONFIG.apn_configuration.pdn_type, ); + const [isDefault, setIsDefault] = useState( + props.apn?.apn_configuration?.is_default || + DEFAULT_APN_CONFIG.apn_configuration.is_default, + ); const onSave = async () => { if (apn.apn_name === '') { @@ -136,6 +142,7 @@ export function ApnEdit(props: Props) { ambr: maxBandwidth, qos_profile: qosProfile, pdn_type: pdnType, + is_default: isDefault, }, }; await ctx.setState(newApn.apn_name, newApn); @@ -255,6 +262,19 @@ export function ApnEdit(props: Props) { checked={qosProfile.preemption_capability} /> + + setIsDefault(target.checked)} + name="isDefault" + color="primary" + /> + } + label="Is Default" + /> + ', () => { const newApn = { apn_configuration: { ambr: {max_bandwidth_dl: 1000000, max_bandwidth_ul: 1000000}, + is_default: false, pdn_type: 1, qos_profile: { class_id: 9, @@ -247,6 +250,7 @@ describe('', () => { const newApn = { apn_configuration: { ambr: {max_bandwidth_dl: 1000000, max_bandwidth_ul: 1000000}, + is_default: false, pdn_type: 0, qos_profile: { class_id: 8, diff --git a/nms/app/views/traffic/__tests__/TrafficOverviewTest.tsx b/nms/app/views/traffic/__tests__/TrafficOverviewTest.tsx index 60b0702e19dc..3ff165ab1a23 100644 --- a/nms/app/views/traffic/__tests__/TrafficOverviewTest.tsx +++ b/nms/app/views/traffic/__tests__/TrafficOverviewTest.tsx @@ -39,6 +39,7 @@ const apns = { preemption_vulnerability: false, priority_level: 15, }, + is_default: false, }, apn_name: 'apn_0', }, @@ -54,6 +55,7 @@ const apns = { preemption_vulnerability: false, priority_level: 10, }, + is_default: false, }, apn_name: 'apn_1', }, diff --git a/nms/generated/api.ts b/nms/generated/api.ts index e40084f3fbe0..113e3db401ca 100644 --- a/nms/generated/api.ts +++ b/nms/generated/api.ts @@ -340,6 +340,12 @@ export interface ApnConfiguration { * @memberof ApnConfiguration */ 'ambr': AggregatedMaximumBitrate; + /** + * Is this the default APN? + * @type {boolean} + * @memberof ApnConfiguration + */ + 'is_default'?: boolean; /** * Value identifier for PDN type (0=IPv4 1=IPv6 2=IPv4v6 3=IPv4orv6) * @type {number} diff --git a/nms/server/util/asyncHandler.ts b/nms/server/util/asyncHandler.ts index f278d5e1e726..6714dcb0a35c 100644 --- a/nms/server/util/asyncHandler.ts +++ b/nms/server/util/asyncHandler.ts @@ -17,7 +17,13 @@ interface Query { [key: string]: undefined | string | Array | Query | Array; } -type AsyncHandler = ( +type AsyncHandler< + P, + ResBody, + ReqBody, + ReqQuery, + Locals extends Record +> = ( req: Request, res: Response, next: NextFunction, diff --git a/orc8r/cloud/api/v1/go/models/apn_configuration.go b/orc8r/cloud/api/v1/go/models/apn_configuration.go index 789ce75be8f1..1f8a51729daa 100644 --- a/orc8r/cloud/api/v1/go/models/apn_configuration.go +++ b/orc8r/cloud/api/v1/go/models/apn_configuration.go @@ -24,6 +24,9 @@ type APNConfiguration struct { // qos profile // Required: true QosProfile *QosProfile `json:"qos_profile"` + + // is default + IsDefault *bool `json:"is_default,omitempty"` } // Validate validates this apn configuration diff --git a/orc8r/cloud/go/services/obsidian/swagger/v1/swagger.yml b/orc8r/cloud/go/services/obsidian/swagger/v1/swagger.yml index 596a2934bf58..8c19ab1047e6 100644 --- a/orc8r/cloud/go/services/obsidian/swagger/v1/swagger.yml +++ b/orc8r/cloud/go/services/obsidian/swagger/v1/swagger.yml @@ -6690,6 +6690,10 @@ definitions: properties: ambr: $ref: '#/definitions/aggregated_maximum_bitrate' + is_default: + default: false + description: Is this the default APN? + type: boolean pdn_type: description: Value identifier for PDN type (0=IPv4 1=IPv6 2=IPv4v6 3=IPv4orv6) enum: