From 97483ec95dfa87f140d801c54586e6c6692a1758 Mon Sep 17 00:00:00 2001 From: Stephen Wong Date: Wed, 1 Nov 2023 08:20:46 +0000 Subject: [PATCH 1/4] Add NFTopology Controller doc --- operators/nftopology/README.md | 154 ++++++++++++++++++ operators/nftopology/img/nftopology-crd.jpg | Bin 0 -> 36567 bytes .../nftopology/img/nftopology-functional.jpg | Bin 0 -> 41161 bytes 3 files changed, 154 insertions(+) create mode 100644 operators/nftopology/README.md create mode 100644 operators/nftopology/img/nftopology-crd.jpg create mode 100644 operators/nftopology/img/nftopology-functional.jpg diff --git a/operators/nftopology/README.md b/operators/nftopology/README.md new file mode 100644 index 00000000..188d4345 --- /dev/null +++ b/operators/nftopology/README.md @@ -0,0 +1,154 @@ +# NFTopology Controller +- Author: @s3wong +- Status: Work-in-Progress +- Approver: + +## Description +NFTopology controller (NTC) takes a network topology centric input and kickstarts the Nephio automation process via generating a PackageVariantSet (PVS) custom resource, and as the associated packages are being deployed, NTC continuously updates statuses to reflect state of deployment of these packages. Primarily, NTC serves as example application to utilize Nephio primitives for network function automations. + +![NFTopology High Level Functional Diagram](./img/nftopology-functional.jpg) + +NTC contains two controllers, one watches the NFTopology custom resource, and reconcile that intent via constructing a number of PVSs; the other controller watches PackageRevision resources in the management cluster to determine what NF package has been deployed, and updates the NFTopology status accordingly. + +## NFTopology CRD +This is the definition of the NFTopology custom resource: + +```go +// NFInterface defines the specification of network attachment points of a NF +type NFInterface struct { + // Name of the network attachment point + Name string `json:"name" yaml:"name"` + + // NetworkInstanceRef is a reference to NetworkInstance. Two NF with attachment to + // the same NetworkInstance is considered connected neighbors + NetworkInstanceName string `json:"networkInstanceName" yaml:"networkInstanceName"` +} + +// PackageRevisionReference is a temporary replica of PackageRevisionReference used for the +// ONE Summit +type PackageRevisionReference struct { + // Namespace is the namespace for both the repository and package revision + // +optional + Namespace string `json:"namespace,omitempty"` + + // Repository is the name of the repository containing the package + RepositoryName string `json:"repository"` + + // PackageName is the name of the package for the revision + PackageName string `json:"packageName"` + + // Revision is the specific version number of the revision of the package + Revision string `json:"revision"` +} +// NFTemplate defines the template for deployment of an instance of a NF +type NFTemplate struct { + // NFType specifies the type of NF this template is specifying + NFType string `json:"nfType" yaml:"nfType"` + + // NFPackageRef specifies the upstream package reference for this NFTemplate + NFPackageRef PackageRevisionReference `json:"nfPackageRef" yaml:"nfPackageRef"` + + // Capacity specifies the NF capacity profile for this NF instance + Capacity nephioreqv1alpha1.CapacitySpec `json:"capacity,omitempty" yaml:"capacity,omitempty"` + + // NFInterfaces + NFInterfaces []NFInterface `json:"nfInterfaces,omitempty" yaml:"nfInterfaces,omitempty"` +} + +type NFInstance struct { + // Name specifies the name of this NFInstance + Name string `json:"name" yaml:"name"` + + // ClusterSelector specifies the matching labels for the NF instance to be instantiated + ClusterSelector metav1.LabelSelector `json:"clusterSelector" yaml:"clusterSelector"` + + // NFTemplate specifies the template of the NF to be deployed when a cluster matches + // the selector above + NFTemplate NFTemplate `json:"nfTemplate" yaml:"nfTemplate"` +} + +type NFTopologySpec struct { + NFInstances []NFInstance `json:"nfInstances" yaml:"nfInstances"` +} + +// NFTopologyStatus defines the observed state of NFTopology +type NFTopologyStatus struct { + // Number of NFs deployed for this topology + NumNFDeployed int32 `json:"numNFDeployed,omitempty"` + + // Current service state of the NF. + Conditions []nephiodeployv1alpha1.NFDeploymentConditionType `json:"conditions,omitempty"` + + // Detail on the deployed instances. + NFInstances []NFDeployedInstance `json:"nfInstances,omitempty" yaml:"nfInstances,omitempty"` +} +``` +where NFDeployedInstance is defined as: + +```go +type NFConnectivity struct { + // peer NF's Id (see NFInstance struct below) + NeighborName string `json:"neighborName,omitempty" yaml:"neighborName,omitempty"` +} + +// NFDeployedInstance defines an NF instance that is deployed +type NFDeployedInstance struct { + // unique ID for this NF instance + ID string `json:"id,omitempty" yaml:"id,omitempty"` + // name of workload cluster where the NF instance is to be deployed + ClusterName string `json:"clusterName,omitempty" yaml:"clusterName,omitempty"` + // type of NF, example: amf, smf, upf + NFType string `json:"nfType,omitempty" yaml:"nfType,omitempty"` + // list of connected NF instances to this NF instance + Connectivities []NFConnectivity `json:"connectivities,omitempty" yaml:"connectivities,omitempty"` +} +``` + +The concept behind the NFTopology custom resource is denoted by the following diagram: + +![NFTopology CRD](./img/nftopology-crd.jpg) + +Note that the NetworkInstance field is static, i.e., user defined a fixed NetworkInstance resource to be linked to a template, whereas the actual NF instance is dynamic --- that is, the actuation of the instance of the NF is based off of a cluster matching some labels, but the NetworkInstace this NF specification will be attaching to is statically configured. Note that each NFInstance would only create one instance per each cluster matching a label, and that the NFNetworkInstance does **NOT** define the network, it is merely +a placeholder to denote a connectivity between an instance's attachment point to another instance's attachment point + +## NFTopology to PackageVariantSet CR Mapping +NTC kickstarts the hydration process via applying the **PackageVariantSet** (PVS) CR to management cluster. The following table denotes which fields from NFTopology (and its associated struct) will be used to populate the PVS CR: + +| NFTopology or associated | PVS Spec field | +|:------------------------ | :------------- | +| NFTopology.NFInstance.NFTemplate.PackageRevisionRef | Upstream (*Tag* is missing from PackageRevisionRef... Revision?) | +| NFTopology.NFInstance.ClusterSelector | Targets[0].Repositories | +| {nf-deployment-name : *name of NFTopology CR* | Labels[] | + +The assumption here is that (at least a subset of) the ClusterSelector labels will be used to also label the repositories, i.e., target workload cluster with label `region == x; cluster-role == y` will also be part of the labels to use on a deployment repo of a target workload cluster. + +NFTopology CR has capacity input which needs to be passed through PVS to the actual NF packages. This can be accomplished via the kpt provided function **search-replace** where the capacity input from NFTopology CR can be passed to the cloned NF package, which also contains capacity.yaml. + +NTC will also need to associate all the to-be-fan'ed-out packages with this deployment. In PVS, this is allowed as a set of labels to be passed to PVS controller, which will then propagate those labels to PackageVariant (PV) CRs, and subsequently the PackageRevision (PR) CRs. NTC will then watch for all the PR resources created, and associated them with which NFTopology specification via these labels. + +NTC also adds a label to each of the associated PVCs for connectivity association. This will lead to each resulting PackageRevision to also carry these labels. + + +## Tracking Packages +NTC will embed a label 'nf-deployment-name', which is set to NFToplogy CR's own name; PackageVariantSet and PackageVariant controllers will propagate this label to all the PackageRevision resources associated with the "fan-out"ed packages. + +NTC watches over all PackageRevision resources in the management cluster, and maps the NFTopology intent to the number of deployed NF resources via tracking corresponding PackageRevision resources. As each PackageRevision resource gets to *PUBLISHED* state, NTC would update the **NFInstances** field of its status to reflect on deployed NF package. + +NTC will also extract the number of pending conditions from each of the PR as a display to user(s) who want to continuously examine the lifecycle of the packages, and which conditions are gating a particular package from being deployed. NTC essentially tracks status of package deployment for all NF instances specified derived from the NFTopology intent; however, NTC does **NOT** track the progress of the deployment once the package is pushed to the workload cluster. + +## Constructing NFInstances of NFTopology Status +NTC only tracks those NF instances that are successfully deployed, i.e., the corresponding cloned and hydrated / specialized package is merged to the **main** branch on the corresponding deployment repo for that target workload cluster. NTC would first create the initial *NFDeployTopology* CR when the first package merged, then update the CR as every subsequent package got merged. + +As each NF package got merged, the existing NFDeployedInstance's Connectivities field would change. This relationship is keyed off of the NetworkInstance resource; NetworkInstance specifies for each cluster matching the input label, a corresponding NF specified by NFTemplate would be deployed. For each NFAttachment defined on this NFInstance, there is a NetworkInstance defined --- and for any other NFInstance that has NFAttachment +attached to this same network, it means the two NFInstances are connected. From this, NTC can construct the connectivities part of the NFDeployedTopology struct. The rest of the NFDeployedToplogy basically mapped one to one with different parts of NFTopology, as denoted by the following table: + +| NFTopology | NFDeployedTopology | +|:---------- | :----------------- | +| **self-generated** | *Id* | +| deployment repo | *ClusterName* | +| NFTemplate.NFType | *NFType* | +| Class.Vendor | *NFVendor* | +| Class.Version | *NFVersion* | + +## Reference +Package Variant Controller: [doc](https://github.com/GoogleContainerTools/kpt/blob/a58c5c080787de693382ffd6936b73e9aed116c8/docs/design-docs/08-package-variant.md) diff --git a/operators/nftopology/img/nftopology-crd.jpg b/operators/nftopology/img/nftopology-crd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d0a198119cccfcedd11fafad55873d48c98155f GIT binary patch literal 36567 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 G)BgvgL^VPH literal 0 HcmV?d00001 diff --git a/operators/nftopology/img/nftopology-functional.jpg b/operators/nftopology/img/nftopology-functional.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d583f5b045f34ae31ad1a79c5fca8b604b5c9551 GIT binary patch literal 41161 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 FzW@XNcvb)a literal 0 HcmV?d00001 From 7d3a3af228eb676ed562a627ab61704db054348f Mon Sep 17 00:00:00 2001 From: Stephen Wong Date: Wed, 1 Nov 2023 08:23:06 +0000 Subject: [PATCH 2/4] NFDeployedTopology now NFDeployedInstance --- operators/nftopology/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/operators/nftopology/README.md b/operators/nftopology/README.md index 188d4345..25ec5a00 100644 --- a/operators/nftopology/README.md +++ b/operators/nftopology/README.md @@ -140,15 +140,13 @@ NTC will also extract the number of pending conditions from each of the PR as a NTC only tracks those NF instances that are successfully deployed, i.e., the corresponding cloned and hydrated / specialized package is merged to the **main** branch on the corresponding deployment repo for that target workload cluster. NTC would first create the initial *NFDeployTopology* CR when the first package merged, then update the CR as every subsequent package got merged. As each NF package got merged, the existing NFDeployedInstance's Connectivities field would change. This relationship is keyed off of the NetworkInstance resource; NetworkInstance specifies for each cluster matching the input label, a corresponding NF specified by NFTemplate would be deployed. For each NFAttachment defined on this NFInstance, there is a NetworkInstance defined --- and for any other NFInstance that has NFAttachment -attached to this same network, it means the two NFInstances are connected. From this, NTC can construct the connectivities part of the NFDeployedTopology struct. The rest of the NFDeployedToplogy basically mapped one to one with different parts of NFTopology, as denoted by the following table: +attached to this same network, it means the two NFInstances are connected. From this, NTC can construct the connectivities part of the NFDeployedInstance struct. The rest of the NFDeployedToplogy basically mapped one to one with different parts of NFTopology, as denoted by the following table: -| NFTopology | NFDeployedTopology | +| NFTopology | NFDeployedInstance | |:---------- | :----------------- | | **self-generated** | *Id* | | deployment repo | *ClusterName* | | NFTemplate.NFType | *NFType* | -| Class.Vendor | *NFVendor* | -| Class.Version | *NFVersion* | ## Reference Package Variant Controller: [doc](https://github.com/GoogleContainerTools/kpt/blob/a58c5c080787de693382ffd6936b73e9aed116c8/docs/design-docs/08-package-variant.md) From 8fb0d209735551ea80d7bbe943b4abaeb7771fa8 Mon Sep 17 00:00:00 2001 From: Stephen Wong Date: Tue, 14 Nov 2023 23:46:22 +0000 Subject: [PATCH 3/4] incorporate review comments --- operators/nftopology/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/operators/nftopology/README.md b/operators/nftopology/README.md index 25ec5a00..0c09c9c3 100644 --- a/operators/nftopology/README.md +++ b/operators/nftopology/README.md @@ -4,11 +4,11 @@ - Approver: ## Description -NFTopology controller (NTC) takes a network topology centric input and kickstarts the Nephio automation process via generating a PackageVariantSet (PVS) custom resource, and as the associated packages are being deployed, NTC continuously updates statuses to reflect state of deployment of these packages. Primarily, NTC serves as example application to utilize Nephio primitives for network function automations. +The NFTopology controller (NTC) takes a network topology centric input and kickstarts the Nephio automation process by generating a PackageVariantSet (PVS) custom resource. As the associated packages are being deployed, NTC continuously updates their status fields to reflect the state of deployment of these packages. Primarily, NTC serves as example application that utilizes Nephio primitives for network function automations. ![NFTopology High Level Functional Diagram](./img/nftopology-functional.jpg) -NTC contains two controllers, one watches the NFTopology custom resource, and reconcile that intent via constructing a number of PVSs; the other controller watches PackageRevision resources in the management cluster to determine what NF package has been deployed, and updates the NFTopology status accordingly. +The NTC contains two controllers. The first controller watches the NFTopology custom resource and reconciles that intent by constructing a number of PVSs. The second controller watches PackageRevision resources in the management cluster to determine what NF packages have been deployed and updates the NFTopology status accordingly. ## NFTopology CRD This is the definition of the NFTopology custom resource: @@ -24,8 +24,7 @@ type NFInterface struct { NetworkInstanceName string `json:"networkInstanceName" yaml:"networkInstanceName"` } -// PackageRevisionReference is a temporary replica of PackageRevisionReference used for the -// ONE Summit +// PackageRevisionReference defines a reference to a package type PackageRevisionReference struct { // Namespace is the namespace for both the repository and package revision // +optional @@ -108,11 +107,11 @@ The concept behind the NFTopology custom resource is denoted by the following di ![NFTopology CRD](./img/nftopology-crd.jpg) -Note that the NetworkInstance field is static, i.e., user defined a fixed NetworkInstance resource to be linked to a template, whereas the actual NF instance is dynamic --- that is, the actuation of the instance of the NF is based off of a cluster matching some labels, but the NetworkInstace this NF specification will be attaching to is statically configured. Note that each NFInstance would only create one instance per each cluster matching a label, and that the NFNetworkInstance does **NOT** define the network, it is merely -a placeholder to denote a connectivity between an instance's attachment point to another instance's attachment point +Note that the NetworkInstance field is static, i.e., the user defined a fixed NetworkInstance resource to be linked to a template, whereas the actual NF instance is dynamic --- that is, the actuation of the instance of the NF is based on a cluster matching some labels, but the NetworkInstace that this NF specification will be attaching to is statically configured. Note that each NFInstance would only create one instance per each cluster matching a label, and that the NFNetworkInstance does **NOT** define the network, it is merely +a placeholder to denote connectivity between an instance's attachment point and another instance's attachment point ## NFTopology to PackageVariantSet CR Mapping -NTC kickstarts the hydration process via applying the **PackageVariantSet** (PVS) CR to management cluster. The following table denotes which fields from NFTopology (and its associated struct) will be used to populate the PVS CR: +NTC kickstarts the hydration process by applying the **PackageVariantSet** (PVS) CR to management cluster. The following table denotes which fields from NFTopology (and its associated struct) will be used to populate the PVS CR: | NFTopology or associated | PVS Spec field | |:------------------------ | :------------- | @@ -132,14 +131,14 @@ NTC also adds a label to each of the associated PVCs for connectivity associatio ## Tracking Packages NTC will embed a label 'nf-deployment-name', which is set to NFToplogy CR's own name; PackageVariantSet and PackageVariant controllers will propagate this label to all the PackageRevision resources associated with the "fan-out"ed packages. -NTC watches over all PackageRevision resources in the management cluster, and maps the NFTopology intent to the number of deployed NF resources via tracking corresponding PackageRevision resources. As each PackageRevision resource gets to *PUBLISHED* state, NTC would update the **NFInstances** field of its status to reflect on deployed NF package. +NTC watches over all PackageRevision resources in the management cluster, and maps the NFTopology intent to the number of deployed NF resources by tracking the corresponding PackageRevision resources. As each PackageRevision resource gets to *PUBLISHED* state, NTC will update the **NFInstances** field of its status to reflect on deployed NF package. NTC will also extract the number of pending conditions from each of the PR as a display to user(s) who want to continuously examine the lifecycle of the packages, and which conditions are gating a particular package from being deployed. NTC essentially tracks status of package deployment for all NF instances specified derived from the NFTopology intent; however, NTC does **NOT** track the progress of the deployment once the package is pushed to the workload cluster. ## Constructing NFInstances of NFTopology Status NTC only tracks those NF instances that are successfully deployed, i.e., the corresponding cloned and hydrated / specialized package is merged to the **main** branch on the corresponding deployment repo for that target workload cluster. NTC would first create the initial *NFDeployTopology* CR when the first package merged, then update the CR as every subsequent package got merged. -As each NF package got merged, the existing NFDeployedInstance's Connectivities field would change. This relationship is keyed off of the NetworkInstance resource; NetworkInstance specifies for each cluster matching the input label, a corresponding NF specified by NFTemplate would be deployed. For each NFAttachment defined on this NFInstance, there is a NetworkInstance defined --- and for any other NFInstance that has NFAttachment +As each NF package gets merged, the existing NFDeployedInstance's Connectivities field will change. This relationship is keyed off of the NetworkInstance resource; NetworkInstance specifies for each cluster matching the input label, a corresponding NF specified by NFTemplate will be deployed. For each NFAttachment defined on this NFInstance, there is a NetworkInstance defined --- and for any other NFInstance that has NFAttachment attached to this same network, it means the two NFInstances are connected. From this, NTC can construct the connectivities part of the NFDeployedInstance struct. The rest of the NFDeployedToplogy basically mapped one to one with different parts of NFTopology, as denoted by the following table: | NFTopology | NFDeployedInstance | From 46d7b98d9bb68bba2eb91c01c63d75fff21eb942 Mon Sep 17 00:00:00 2001 From: Stephen Wong Date: Tue, 14 Nov 2023 23:49:22 +0000 Subject: [PATCH 4/4] incorporate further review comments --- operators/nftopology/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operators/nftopology/README.md b/operators/nftopology/README.md index 0c09c9c3..0ba6fe46 100644 --- a/operators/nftopology/README.md +++ b/operators/nftopology/README.md @@ -123,7 +123,7 @@ The assumption here is that (at least a subset of) the ClusterSelector labels wi NFTopology CR has capacity input which needs to be passed through PVS to the actual NF packages. This can be accomplished via the kpt provided function **search-replace** where the capacity input from NFTopology CR can be passed to the cloned NF package, which also contains capacity.yaml. -NTC will also need to associate all the to-be-fan'ed-out packages with this deployment. In PVS, this is allowed as a set of labels to be passed to PVS controller, which will then propagate those labels to PackageVariant (PV) CRs, and subsequently the PackageRevision (PR) CRs. NTC will then watch for all the PR resources created, and associated them with which NFTopology specification via these labels. +NTC will also need to associate all the to-be-fan'ed-out packages with this deployment. In PVS, this is allowed as a set of labels to be passed to PVS controller, which will then propagate those labels to PackageVariant (PV) CRs, and subsequently the PackageRevision (PR) CRs. NTC will then watch for all the PR resources created, and associate them with the relevant NFTopology specification via these labels. NTC also adds a label to each of the associated PVCs for connectivity association. This will lead to each resulting PackageRevision to also carry these labels.