Repository: F5Networks/k8s-bigip-ctlr

Issue:
RFE: change log level of certain messages
#### Title
Title of the feature/enhancement request

#### Description

The following is what it can be seen when CIS processes an AS3 ConfigMap with INFO log level:

```
2025/06/26 05:43:21 [INFO] [2025-06-26 05:43:21,547 __main__ INFO] entering inotify loop to watch /tmp/k8s-bigip-ctlr.config936174469/config.json
```

That is, there is INFO at all if a ConfigMap has been found or if a AS3 POST has been sent

Turning log level to DEBUG the information displayed is way too much for regular use, as expected:

```
2025/06/26 10:17:51 [DEBUG] [CORE] Periodic enqueue of Service from Namespace: cis3, svc: app1
2025/06/26 10:17:51 [DEBUG] [CORE] Finished syncing namespace cis3 (103.961µs)
2025/06/26 10:17:51 [DEBUG] [CORE] Extract service via watch-list informer 'cis3'
2025/06/26 10:17:51 [DEBUG] [CORE] Discovered members for service cis3/app1 is [{10.129.1.151 8080  8080  enable 0 0} {10.129.1.152 8080  8080  enable 0 0} {10.129.1.153 8080  8080  enable 0 0}]
2025/06/26 10:17:51 [DEBUG] [CORE] Updated 1 of 0 virtual server configs, deleted 0
2025/06/26 10:17:51 [DEBUG] [CORE] Finished syncing virtual servers with service app1 in namespace cis3 (506.964µs), processed VS 1 out of 2
2025/06/26 10:17:51 [DEBUG] [CORE] Finished syncing virtual servers with service  in namespace cis3 (46.176µs), processed VS 2 out of 2
2025/06/26 10:17:51 [DEBUG] [CORE] Extract service via watch-list informer 'cis3'
2025/06/26 10:17:51 [DEBUG] [CORE] Discovered members for service cis3/app1 is [{10.129.1.151 8080  8080  enable 0 0} {10.129.1.152 8080  8080  enable 0 0} {10.129.1.153 8080  8080  enable 0 0}]
2025/06/26 10:17:51 [DEBUG] [CORE] Updated 0 of 0 virtual server configs, deleted 0
2025/06/26 10:17:51 [DEBUG] [CORE] Finished syncing virtual servers with service app1 in namespace cis3 (549.321µs), processed VS 2 out of 2
2025/06/26 10:17:51 [DEBUG] [CORE] Extract service via watch-list informer 'cis3'
2025/06/26 10:17:51 [DEBUG] [CORE] Discovered members for service cis3/app1 is [{10.129.1.151 8080  8080  enable 0 0} {10.129.1.152 8080  8080  enable 0 0} {10.129.1.153 8080  8080  enable 0 0}]
2025/06/26 10:17:52 [DEBUG] [AS3] Posting AS3 Declaration
2025/06/26 10:17:52 [DEBUG] [AS3] posting request to https://10.1.1.7/mgmt/shared/appsvcs/declare/
2025/06/26 10:18:01 [DEBUG] [AS3] Raw response from Big-IP: map[declaration:{"cis3":{"Shared":{"class":"Application","template":"shared"},"class":"Tenant","defaultRouteDomain":0},"class":"ADC","controls":{"archiveTimestamp":"2025-06-26T10:17:50.168Z","class":"Controls","userAgent":"CIS/v2.19.1 OCP/v4.16.14"},"id":"urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d","label":"CIS Declaration","remark":"Auto-generated by CIS","schemaVersion":"3.52.0","test":{"app1":{"app_pool":{"class":"Pool","members":[{"adminState":"enable","serverAddresses":["10.129.1.151"],"servicePort":8080,"shareNodes":false},{"adminState":"enable","serverAddresses":["10.129.1.152"],"servicePort":8080,"shareNodes":false},{"adminState":"enable","serverAddresses":["10.129.1.153"],"servicePort":8080,"shareNodes":false}],"monitors":["http"]},"class":"Application","serviceMain":{"class":"Service_HTTP","persistenceMethods":[],"pool":"app_pool","virtualAddresses":["10.8.1.183"],"virtualPort":8080},"template":"http"},"class":"Tenant","defaultRouteDomain":0},"updateMode":"selective"} results:[map[code:200 declarationId:urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d host:localhost message:no change runTime:807 tenant:cis3] map[code:200 declarationId:urn:uuid:85626792-9ee7-46bb-8fc8-4ba708cfdc1d host:localhost message:no change runTime:973 tenant:test]]] 
2025/06/26 10:18:01 [DEBUG] [AS3] Response from BIG-IP: code: 200 --- tenant:cis3 --- message: no change
2025/06/26 10:18:01 [DEBUG] [AS3] Response from BIG-IP: code: 200 --- tenant:test --- message: no change

```


#### Actual Problem

When applying configs, the customer is blind if the operation has been succesfull, specially when using ConfigMaps.

#### Solution Proposed

IMHO, with INFO log level there should be some must messages to be shown, for example:

- Indicating that AS3 ConfigMap new/change/delete has been detected
- Indicating the AS3 POST response  (the last two lines in the log above). Adding this info in non ConfigMap mode should be fine, even it is redundant when using resources that return status.



Hints from issue comments:
@trinaths: Created [CONTCNTR-5501] for internal tracking.@mdditt2000: Resolved in CIS 2.20.2 https://clouddocs.f5.com/containers/latest/reference/release-notes.html

Solution patch:
diff --git a/cmd/k8s-bigip-ctlr/main.go b/cmd/k8s-bigip-ctlr/main.go
index 447c673f..3324a8d2 100644
--- a/cmd/k8s-bigip-ctlr/main.go
+++ b/cmd/k8s-bigip-ctlr/main.go
@@ -167,6 +167,8 @@ var (
 	bigIPUsername               *string
 	bigIPPassword               *string
 	bigIPPartitions             *[]string
+	allowedPartitions           *[]string
+	deniedPartitions            *[]string
 	credsDir                    *string
 	as3Validation               *bool
 	sslInsecure                 *bool
@@ -225,6 +227,8 @@ var (
 	userAgentInfo      string
 	multiClusterMode   *string
 	localClusterName   *string
+	allowedList        []string
+	deniedList         []string
 )
 
 func _init() {
@@ -309,6 +313,10 @@ func _init() {
 		"Required, password for the Big-IP user account.")
 	bigIPPartitions = bigIPFlags.StringArray("bigip-partition", []string{},
 		"Required, partition(s) for the Big-IP kubernetes objects.")
+	allowedPartitions = bigIPFlags.StringSlice("allowed-partitions", []string{},
+		"Optional, allowed partition(s) for the Big-IP kubernetes objects.")
+	deniedPartitions = bigIPFlags.StringSlice("denied-partitions", []string{},
+		"Required, denied partition(s) for the Big-IP kubernetes objects.")
 	credsDir = bigIPFlags.String("credentials-directory", "",
 		"Optional, directory that contains the BIG-IP username, password, and/or "+
 			"url files. To be used instead of username, password, and/or url arguments.")
@@ -589,6 +597,37 @@ func verifyArgs() error {
 		}
 	}
 
+	deniedList = nil
+	allowedList = nil
+
+	// Process denied partitions first as it takes precedence over allowed partitions
+	if deniedPartitions != nil && len(*deniedPartitions) != 0 {
+		defaultPartition := (*bigIPPartitions)[0]
+		for _, p := range *deniedPartitions {
+			trimmedPartition := strings.TrimSpace(strings.Trim(strings.TrimSpace(p), "'")) // ex: deniedPartition=   ' test '-> ' test ' -> 'test' -> test
+			if defaultPartition == trimmedPartition {
+				return fmt.Errorf("Default Partition %s cannot be specified in denied partitions list. "+
+					"Please remove it from denied list and run CIS again.", defaultPartition)
+			}
+			deniedList = append(deniedList, trimmedPartition)
+		}
+		log.Debugf("denied-partitions list: %v", deniedList)
+		if allowedPartitions != nil && len(*allowedPartitions) != 0 {
+			log.Warningf("denied-partitions is set to %v, ignoring allowed-partitions", deniedList)
+		}
+	} else if allowedPartitions != nil && len(*allowedPartitions) != 0 {
+		for _, p := range *allowedPartitions {
+			trimmedPartition := strings.TrimSpace(strings.Trim(strings.TrimSpace(p), "'")) // ex: allowedPartition=   ' test '-> ' test ' -> 'test' -> test
+			if controller.CommonPartition == trimmedPartition {
+				return fmt.Errorf("Common cannot be specified in allowed partitions list. " +
+					"Please remove it from allowed list and run CIS again.")
+			}
+			allowedList = append(allowedList, trimmedPartition)
+		}
+		allowedList = append(allowedList, (*bigIPPartitions)[0])
+		log.Debugf("allowed-partitions list: %v", allowedList)
+	}
+
 	if len(*bigIPURL) == 0 && len(*credsDir) == 0 {
 		return fmt.Errorf("Missing BIG-IP URL")
 	}
@@ -1011,6 +1050,8 @@ func initController(
 			LoadBalancerClass:           *loadBalancerClass,
 			ManageLoadBalancerClassOnly: *manageLoadBalancerClassOnly,
 			CustomResourceLabel:         *customResourceLabel,
+			AllowedPartitions:           allowedList,
+			DeniedPartitions:            deniedList,
 		},
 		true,
 		agentParams,
diff --git a/config/apis/cis/v1/types.go b/config/apis/cis/v1/types.go
index e77387f7..6578c3a4 100644
--- a/config/apis/cis/v1/types.go
+++ b/config/apis/cis/v1/types.go
@@ -457,6 +457,7 @@ type ProfileSpec struct {
 	RewriteProfile         string            `json:"rewriteProfile,omitempty"`
 	PersistenceProfile     string            `json:"persistenceProfile,omitempty"`
 	LogProfiles            []string          `json:"logProfiles,omitempty"`
+	RequestLogProfile      string            `json:"requestLogProfile,omitempty"`
 	ProfileL4              string            `json:"profileL4,omitempty"`
 	ProfileMultiplex       string            `json:"profileMultiplex,omitempty"`
 	HttpMrfRoutingEnabled  *bool             `json:"httpMrfRoutingEnabled,omitempty"`
diff --git a/docs/RELEASE-NOTES.rst b/docs/RELEASE-NOTES.rst
index 5f6d7a38..c3571de3 100644
--- a/docs/RELEASE-NOTES.rst
+++ b/docs/RELEASE-NOTES.rst
@@ -14,11 +14,14 @@ Added Functionality
         * Issue 3852 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3852>`_: support for static pool members in VirtualServer
         * Issue 3865 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3865>`_: support for secured and unsecured httpProfiles in policy CRD
         * Issue 3927 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3927>`_: feature request: customized virtualServerName for IngressLink resource
+        * Support request log profile in policy CRD
+        * Issue 3932 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3932>`_: support for allowed and denied partitions lists for Virtual Server CRD
 
 Bug Fixes
 ````````````
 * Issue 3719 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3719>`_: Fix shared static routes override each other with multiple CIS instances writing to Common Partition
 * Issue 3852 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3852>`_: Improve logging when BIG-IP is not reachable during pod initialization
+* Issue 3887 <https://github.com/F5Networks/k8s-bigip-ctlr/issues/3887>`_: RFE: change log level of certain messages
 
 Upgrade notes
 ``````````````
diff --git a/docs/config_examples/customResource/Policy/README.md b/docs/config_examples/customResource/Policy/README.md
index 40de28fc..157e4b3e 100644
--- a/docs/config_examples/customResource/Policy/README.md
+++ b/docs/config_examples/customResource/Policy/README.md
@@ -67,25 +67,26 @@ This is used to apply existing BIG-IP profiles and policy with Routes, Virtual S
 
 ### Profile Components
 
-| Parameter             | Type           | Required | Default                                                           | Description                                                                                                                                                                                                                                |
-|-----------------------|----------------|----------|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| tcp                   | Object         | Optional | N/A                                                               | TCP Client & Server Profiles                                                                                                                                                                                                               |
-| udp                   | String         | Optional | N/A                                                               | Pathname of existing BIG-IP UDP profile.                                                                                                                                                                                                   |
-| http                  | String         | Optional | N/A                                                               | Pathname of existing BIG-IP HTTP profile.                                                                                                                                                                                                  |
-| httpProfiles          | Object         | Optional | N/A                                                               | Pathname of existing BIG-IP HTTP profile for secured and unsecured virtual server.                                                                                                                                                         |
-| https                 | String         | Optional | N/A                                                               | Pathname of existing BIG-IP SSL profile.                                                                                                                                                                                                   |
-| http2                 | Object         | Optional | N/A                                                               | HTTP2 Client & Server Profiles                                                                                                                                                                                                             |
-| logProfiles           | List of string | Optional | N/A                                                               | Pathname of existing BIG-IP log profile.                                                                                                                                                                                                   |
-| persistenceProfile    | String         | Optional | VirtualServer uses `cookie` TransportServer uses `source-address` | CIS uses the AS3 default persistence profile. VirtualServer or TransportServer CRD resource takes precedence over Policy CRD resource. Allowed values are existing BIG-IP Persistence profiles and custom Persistence profiles.            |
-| profileMultiplex      | String         | Optional | N/A                                                               | CIS uses the AS3 default profileMultiplex profile. Allowed values are existing BIG-IP profileMultiplex profiles.                                                                                                                           |
-| profileL4             | String         | Optional | basic                                                             | The default value is `basic` but it is not configurable if the profileL4 spec is not included in TS or Policy CR. Transport CRD resource takes precedence over Policy CRD resource. Allowed values are existing BIG-IP profileL4 profiles. |
-| httpMrfRoutingEnabled | Boolean        | Optional | N/A                                                               | Reference to Http mrf router on BIGIP.                                                                                                                                                                                                     |
-| sslProfiles           | Object         | Optional | N/A                                                               | Reference to existing ssl profiles on BIGIP. Policy sslProfiles will have the highest precedence and will override route level profiles                                                                                                    |
-| analyticsProfiles     | Object         | Optional | N/A                                                               | Configures different analytics profiles on BIGIP virtual server.                                                                                                                                                                           |
-| profileWebSocket      | String         | Optional | N/A                                                               | Reference to existing BIG-IP websocket profile                                                                                                                                                                                             |
-| htmlProfile           | String         | Optional | NA                                                                | Pathname of existing BIG-IP HTML profile. VirtualServer CRD resource takes precedence over Policy CRD. Allowed values are existing BIG-IP HTML profiles.                                                                                   |
-| ftpProfile      | String         | Optional | N/A                                                               | Reference to existing BIG-IP FTP profile and is supported only for Transport Server                                                                                                                                                        |
-| httpCompressionProfile | String         | Optional | N/A                                                               | Reference to existing BIG-IP HTTP Compression profile and is supported only for Virtual Server                                                                                                                                             |
+| Parameter              | Type           | Required | Default                                                           | Description                                                                                                                                                                                                                              |
+|------------------------|----------------|----------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| tcp                    | Object         | Optional | N/A                                                               | TCP Client & Server Profiles                                                                                                                                                                                                             |
+| udp                    | String         | Optional | N/A                                                               | Pathname of existing BIG-IP UDP profile.                                                                                                                                                                                                 |
+| http                   | String         | Optional | N/A                                                               | Pathname of existing BIG-IP HTTP profile.                                                                                                                                                                                                |
+| httpProfiles           | Object         | Optional | N/A                                                               | Pathname of existing BIG-IP HTTP profile for secured and unsecured virtual server.                                                                                                                                                       |
+| https                  | String         | Optional | N/A                                                               | Pathname of existing BIG-IP SSL profile.                                                                                                                                                                                                 |
+| http2                  | Object         | Optional | N/A                                                               | HTTP2 Client & Server Profiles                                                                                                                                                                                                           |
+| logProfiles            | List of string | Optional | N/A                                                               | Pathname of existing BIG-IP log profile.                                                                                                                                                                                                 |
+| requestLogProfile      | String         | Optional | N/A                                                               | Pathname of existing BIG-IP Request Log profile.                                                                                                                                                                                         |
+| persistenceProfile     | String         | Optional | VirtualServer uses `cookie` TransportServer uses `source-address` | CIS uses the AS3 default persistence profile. VirtualServer or TransportServer CRD resource takes precedence over Policy CRD resource. Allowed values are existing BIG-IP Persistence profiles and custom Persistence profiles.          |
+| profileMultiplex       | String         | Optional | N/A                                                               | CIS uses the AS3 default profileMultiplex profile. Allowed values are existing BIG-IP profileMultiplex profiles.                                                                                                                         |
+| profileL4              | String         | Optional | basic                                                             | The default value is `basic` but it is not configurable if the profileL4 spec is not included in TS or Policy CR. Transport CRD resource takes precedence over Policy CRD resource. Allowed values are existing BIG-IP profileL4 profiles. |
+| httpMrfRoutingEnabled  | Boolean        | Optional | N/A                                                               | Reference to Http mrf router on BIGIP.                                                                                                                                                                                                   |
+| sslProfiles            | Object         | Optional | N/A                                                               | Reference to existing ssl profiles on BIGIP. Policy sslProfiles will have the highest precedence and will override route level profiles                                                                                                  |
+| analyticsProfiles      | Object         | Optional | N/A                                                               | Configures different analytics profiles on BIGIP virtual server.                                                                                                                                                                         |
+| profileWebSocket       | String         | Optional | N/A                                                               | Reference to existing BIG-IP websocket profile                                                                                                                                                                                           |
+| htmlProfile            | String         | Optional | NA                                                                | Pathname of existing BIG-IP HTML profile. VirtualServer CRD resource takes precedence over Policy CRD. Allowed values are existing BIG-IP HTML profiles.                                                                                 |
+| ftpProfile             | String         | Optional | N/A                                                               | Reference to existing BIG-IP FTP profile and is supported only for Transport Server                                                                                                                                                      |
+| httpCompressionProfile | String         | Optional | N/A                                                               | Reference to existing BIG-IP HTTP Compression profile and is supported only for Virtual Server                                                                                                                                           |
 
 **Note**:
 * sslProfiles is only applicable to NextGen routes
diff --git a/docs/config_examples/customResource/Policy/request-log-profile.yaml b/docs/config_examples/customResource/Policy/request-log-profile.yaml
new file mode 100644
index 00000000..fd65bb89
--- /dev/null
+++ b/docs/config_examples/customResource/Policy/request-log-profile.yaml
@@ -0,0 +1,33 @@
+apiVersion: cis.f5.com/v1
+kind: Policy
+metadata:
+  labels:
+    f5cr: "true"
+  name: cr-policy1
+  namespace: default
+spec:
+  iRuleList: []
+  iRules:
+    insecure: /Common/test_rule1
+    priority: high
+    secure: /Common/test_rule1
+  l3Policies:
+    allowVlans:
+      - /Common/external
+    firewallPolicy: /Common/firewallPolicy1
+    ipIntelligencePolicy: /Common/ipIntelligencePolicy1
+  l7Policies:
+    waf: /Common/WAF_Policy1
+  profiles:
+    ftpProfile: /Common/ftpProfile1
+    htmlProfile: /Common/htmlProfile1
+    http: /Common/httpProfile1
+    http2:
+      client: /Common/http2Profile1
+    logProfiles:
+      - /Common/logProfile2
+    persistenceProfile: /Common/customPersistence1
+    requestLogProfile: /Common/request-log
+    rewriteProfile: /Common/rewriteProfile1
+    tcp:
+      client: /Common/tcpProfile1
\ No newline at end of file
diff --git a/docs/config_examples/customResource/VirtualServer/partition/README.md b/docs/config_examples/customResource/VirtualServer/partition/README.md
index 413fbe55..3a0da1f4 100644
--- a/docs/config_examples/customResource/VirtualServer/partition/README.md
+++ b/docs/config_examples/customResource/VirtualServer/partition/README.md
@@ -17,3 +17,7 @@ partition: dev
 ## vs-with-partition.yaml
 
 By deploying this yaml file in your cluster, CIS will create Virtual Server in dev partition on BIG-IP
+
+#### Note:
+* If allowed-partitions deployment parameter is provided, ensure this Virtual Server partition is included in the list.
+* If denied-partitions deployment parameter is provided, ensure this Virtual Server partition is not included in the list.
diff --git a/docs/config_examples/customResourceDefinitions/incubator/customresourcedefinitions.yml b/docs/config_examples/customResourceDefinitions/incubator/customresourcedefinitions.yml
index 0be6d6c6..1e2aa720 100644
--- a/docs/config_examples/customResourceDefinitions/incubator/customresourcedefinitions.yml
+++ b/docs/config_examples/customResourceDefinitions/incubator/customresourcedefinitions.yml
@@ -1410,6 +1410,9 @@ spec:
                         type: string
                         pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)*([-A-z0-9._\s]+\/?)*$'
                       type: array
+                    requestLogProfile:
+                      type: string
+                      pattern: '^\/[a-zA-Z]+([A-z0-9-_+]+\/)+([-A-z0-9_.:]+\/?)*$'
                     httpMrfRoutingEnabled:
                       type: boolean
                     sslProfiles:
diff --git a/pkg/agent/as3/as3ConfigMap.go b/pkg/agent/as3/as3ConfigMap.go
index 82ac8d75..cee5fddc 100644
--- a/pkg/agent/as3/as3ConfigMap.go
+++ b/pkg/agent/as3/as3ConfigMap.go
@@ -33,7 +33,9 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 	// Process rscCfgMap if present in Resource Request
 	for _, rscCfgMap := range am.ResourceRequest.AgentCfgmaps {
 		cfgmapType, ok := am.isValidConfigmap(rscCfgMap)
+		log.Debugf("[AS3] isValidConfigmap result: cfgmapType=%v, ok=%v", cfgmapType, ok)
 		if !ok {
+			log.Debugf("[AS3] Skipping invalid ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 			continue
 		}
 
@@ -43,6 +45,7 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 			// So that the tenants will be configured with empty config
 			// while preparing unified declaration so that these partitions will deleted
 			if rscCfgMap.Operation == OprTypeDelete {
+				log.Debugf("[AS3] Skipping deleted AS3 ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 				continue
 			}
 			cfgmap := &AS3ConfigMap{
@@ -59,7 +62,9 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 					// Adding this condition as cfgmap.Validated flag is only used in filter-tenant case
 					if am.FilterTenants {
 						cfgmap.Validated = false
+						log.Debugf("[AS3] Validation failed, marking ConfigMap as not validated: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 					} else {
+						log.Debugf("[AS3] Validation failed, skipping ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 						continue
 					}
 
@@ -67,17 +72,21 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 			}
 
 			tenantMap, endPoints, err := am.processCfgMap(rscCfgMap)
+			log.Debugf("[AS3] processCfgMap result: tenantMap=%+v, endPoints=%+v, err=%v", tenantMap, endPoints, err)
 			// Skip processing further if error encountered while processing configMap
 			if err != nil {
+				log.Errorf("[AS3] Error processing ConfigMap: %v/%v: %v", rscCfgMap.Namespace, rscCfgMap.Name, err)
 				return nil, "", err
 			}
 			if tenantMap == nil {
+				log.Debugf("[AS3] No tenantMap found, skipping ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 				continue
 			}
 
 			cfgmap.config = tenantMap
 			cfgmap.endPoints = endPoints
 			as3Cfgmaps = append(as3Cfgmaps, cfgmap)
+			log.Infof("[AS3][ConfigMap] Added AS3 ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 
 		case OverrideAS3Label:
 			if rscCfgMap.Operation == OprTypeDelete {
@@ -87,8 +96,10 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 				overriderAS3CfgmapData = ""
 			} else {
 				overriderAS3CfgmapData = rscCfgMap.Data
+				log.Debugf("[AS3] Set overriderAS3CfgmapData for: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 			}
 		case StagingAS3Label:
+			log.Debugf("[AS3] Processing StagingAS3Label for: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 			tenants := getTenants(as3Declaration(rscCfgMap.Data), true)
 			cfgmap := &AS3ConfigMap{
 				Name:      rscCfgMap.Name,
@@ -97,13 +108,16 @@ func (am *AS3Manager) prepareResourceAS3ConfigMaps() (
 			}
 			rscCfgMap.Data = am.getTenantObjects(tenants)
 			tenantMap, endPoints, err := am.processCfgMap(rscCfgMap)
+			log.Debugf("[AS3] processCfgMap (staging) result: tenantMap=%+v, endPoints=%+v, err=%v", tenantMap, endPoints, err)
 			// Skip processing further if error encountered while processing configMap
 			if err != nil {
+				log.Errorf("[AS3] Error processing staging ConfigMap: %v/%v: %v", rscCfgMap.Namespace, rscCfgMap.Name, err)
 				return nil, "", err
 			}
 			cfgmap.config = tenantMap
 			cfgmap.endPoints = endPoints
 			as3Cfgmaps = append(as3Cfgmaps, cfgmap)
+			log.Infof("[AS3][ConfigMap] Added staging AS3 ConfigMap: %v/%v", rscCfgMap.Namespace, rscCfgMap.Name)
 		}
 	}
 	return as3Cfgmaps, overriderAS3CfgmapData, nil
diff --git a/pkg/agent/as3/as3Manager.go b/pkg/agent/as3/as3Manager.go
index 82d9b389..a94bab5e 100644
--- a/pkg/agent/as3/as3Manager.go
+++ b/pkg/agent/as3/as3Manager.go
@@ -234,20 +234,30 @@ func (am *AS3Manager) postAS3Declaration(rsReq ResourceRequest) (bool, string, e
 
 	// Process Route or Ingress
 	as3Config.resourceConfig = am.prepareAS3ResourceConfig()
+	log.Debugf("[AS3] Prepared AS3 resource config: %+v", as3Config.resourceConfig)
 
 	var err error
 	// Process all Configmaps (including overrideAS3)
 	as3Config.configmaps, as3Config.overrideConfigmapData, err = am.prepareResourceAS3ConfigMaps()
+	log.Debugf("[AS3] Prepared AS3 configmaps: %+v, overrideConfigmapData: %v", as3Config.configmaps, as3Config.overrideConfigmapData)
 	// Skip posting AS3 declaration if error encountered while processing configMap to avoid possible wrong declaration
 	// getting posted as the pool members may be empty if error is encountered while connecting with api server
 	if err != nil {
+		log.Errorf("[AS3] Error preparing AS3 ConfigMaps: %v", err)
 		return false, "", err
 	}
 	if am.FilterTenants {
+		log.Debugf("[AS3] Updating tenant map for filtered tenants")
 		updateTenantMap(*as3Config)
 	}
 
 	posted, url := am.postAS3Config(*as3Config)
+	log.Debugf("[AS3] postAS3Config result: posted=%v, url=%v", posted, url)
+	if posted {
+		log.Infof("[AS3][POST] AS3 declaration POST succeeded. URL: %v", url)
+	} else {
+		log.Infof("[AS3][POST] AS3 declaration POST failed. URL: %v", url)
+	}
 	return posted, url, nil
 }
 
diff --git a/pkg/agent/as3/postManager.go b/pkg/agent/as3/postManager.go
index eeb9dbb8..d7038770 100644
--- a/pkg/agent/as3/postManager.go
+++ b/pkg/agent/as3/postManager.go
@@ -320,7 +320,7 @@ func (postMgr *PostManager) handleResponseStatusOK(responseMap map[string]interf
 	for _, value := range results {
 		v := value.(map[string]interface{})
 		// log result with code, tenant and message
-		log.Debugf("[AS3] Response from BIG-IP: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
+		log.Infof("[AS3] Response from BIG-IP: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
 	}
 	return true, responseStatusOk
 }
@@ -380,7 +380,7 @@ func (postMgr *PostManager) handleResponseOthers(responseMap map[string]interfac
 		for _, value := range results {
 			v := value.(map[string]interface{})
 			// log result with code, tenant and message
-			log.Errorf("[AS3] Response from BIG-IP: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
+			log.Infof("[AS3] Response from BIG-IP: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
 		}
 	} else if err, ok := (responseMap["error"]).(map[string]interface{}); ok {
 		log.Errorf("[AS3] Big-IP Responded with error code: %v", err["code"])
diff --git a/pkg/controller/as3Handler.go b/pkg/controller/as3Handler.go
index de520a89..24996701 100644
--- a/pkg/controller/as3Handler.go
+++ b/pkg/controller/as3Handler.go
@@ -356,7 +356,7 @@ func (am *AS3Handler) handleResponseStatusOK(responseMap map[string]interface{},
 				code, ok1 := v["code"].(float64)
 				tenant, ok2 := v["tenant"].(string)
 				if ok1 && ok2 {
-					log.Debugf("[AS3]%v Response from BIG-IP: code: %v --- tenant:%v --- message: %v", am.postManagerPrefix, v["code"], v["tenant"], v["message"])
+					log.Infof("[AS3][POST] SUCCESS: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
 					am.updateTenantResponseCode(int(code), cfg, tenant, updateTenantDeletion(tenant, declaration), "")
 				} else {
 					unknownResponse = true
@@ -386,7 +386,7 @@ func (am *AS3Handler) handleMultiStatus(responseMap map[string]interface{}, cfg
 						log.Errorf("%v[AS3]%v Error response from BIG-IP: code: %v --- tenant:%v --- message: %v", getRequestPrefix(cfg.reqMeta.id), am.postManagerPrefix, v["code"], v["tenant"], v["message"])
 					} else {
 						am.updateTenantResponseCode(int(code), cfg, tenant, updateTenantDeletion(tenant, declaration), "")
-						log.Debugf("[AS3]%v Response from BIG-IP: code: %v --- tenant:%v --- message: %v", am.postManagerPrefix, v["code"], v["tenant"], v["message"])
+						log.Infof("[AS3][POST] SUCCESS: code: %v --- tenant:%v --- message: %v", v["code"], v["tenant"], v["message"])
 					}
 				} else {
 					unknownResponse = true
diff --git a/pkg/controller/as3Parser.go b/pkg/controller/as3Parser.go
index 376d1a33..ee8abdb5 100644
--- a/pkg/controller/as3Parser.go
+++ b/pkg/controller/as3Parser.go
@@ -1235,6 +1235,13 @@ func (ap *AS3Parser) processCommonDecl(cfg *ResourceConfig, svc *as3Service) {
 		}
 	}
 
+	//Attach request logging profile
+	if cfg.Virtual.RequestLogProfile != "" {
+		svc.ProfileTrafficLog = &as3ResourcePointer{
+			BigIP: fmt.Sprintf("%v", cfg.Virtual.RequestLogProfile),
+		}
+	}
+
 	//Attach adapt profile
 	if (cfg.Virtual.ProfileAdapt != ProfileAdapt{}) {
 		if cfg.Virtual.ProfileAdapt.Request != "" {
diff --git a/pkg/controller/backend.go b/pkg/controller/backend.go
index 328aaa16..843b5aa3 100644
--- a/pkg/controller/backend.go
+++ b/pkg/controller/backend.go
@@ -56,7 +56,7 @@ func (agent *Agent) PostConfig(rsConfigRequest ResourceConfigRequest) {
 		}
 		log.Debugf("%s[%s]%v Posting ResourceConfigRequest: %+v\n", getRequestPrefix(rsConfigRequest.reqMeta.id), agent.APIHandler.LTM.apiType, agent.APIHandler.LTM.postManagerPrefix, rsConfigRequest)
 	} else {
-		log.Debugf("%s[%s]%v Posting ResourceConfigRequest: %+v\n", getRequestPrefix(rsConfigRequest.reqMeta.id), agent.APIHandler.LTM.apiType, agent.APIHandler.GTM.postManagerPrefix, rsConfigRequest)
+		log.Debugf("%s[%s]%v Posting ResourceConfigRequest: %+v\n", getRequestPrefix(rsConfigRequest.reqMeta.id), agent.APIHandler.GTM.apiType, agent.APIHandler.GTM.postManagerPrefix, rsConfigRequest)
 		agentConfig = agent.GTM.APIHandler.createAPIConfig(rsConfigRequest, false, agent.userAgent, agent.gtmOnSeparateServer)
 		agentConfig.as3APIURL = agent.GTM.APIHandler.getAPIURL([]string{})
 		agentConfig.agentKind = GTMBigIP
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index 3faa4b73..3cdc352e 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -176,6 +176,8 @@ func NewController(params Params, startController bool, agentParams AgentParams,
 		respChan:                    make(chan *agentPostConfig, 1),
 		multiClusterHandler:         NewClusterHandler(params.LocalClusterName),
 		TeemData:                    td,
+		allowedPartitions:           make(map[string]struct{}),
+		deniedPartitions:            make(map[string]struct{}),
 	}
 	if handler == nil {
 		ctlr.RequestHandler = ctlr.NewRequestHandler(agentParams, baseAPIHandler)
@@ -184,6 +186,14 @@ func NewController(params Params, startController bool, agentParams AgentParams,
 	}
 	log.Debug("Controller Created")
 
+	// Set allowed and denied partitions
+	for _, ap := range params.AllowedPartitions {
+		ctlr.allowedPartitions[ap] = struct{}{}
+	}
+	for _, dp := range params.DeniedPartitions {
+		ctlr.deniedPartitions[dp] = struct{}{}
+	}
+
 	ctlr.resourceQueue = workqueue.NewNamedRateLimitingQueue(
 		workqueue.DefaultControllerRateLimiter(), "nextgen-resource-controller")
 	clusterConfig := newClusterConfig()
diff --git a/pkg/controller/resourceConfig.go b/pkg/controller/resourceConfig.go
index 7f1cf7c4..16c9438f 100644
--- a/pkg/controller/resourceConfig.go
+++ b/pkg/controller/resourceConfig.go
@@ -3076,6 +3076,9 @@ func (ctlr *Controller) handleVSResourceConfigForPolicy(
 	if len(plc.Spec.Profiles.LogProfiles) > 0 {
 		rsCfg.Virtual.LogProfiles = append(rsCfg.Virtual.LogProfiles, plc.Spec.Profiles.LogProfiles...)
 	}
+	if plc.Spec.Profiles.RequestLogProfile != "" {
+		rsCfg.Virtual.RequestLogProfile = plc.Spec.Profiles.RequestLogProfile
+	}
 	var iRule []string
 	// Profiles common for both HTTP and HTTPS
 	// service_HTTP supports profileTCP and profileHTTP
diff --git a/pkg/controller/types.go b/pkg/controller/types.go
index acc2497f..327b16c4 100644
--- a/pkg/controller/types.go
+++ b/pkg/controller/types.go
@@ -105,6 +105,8 @@ type (
 		clusterAdminState           map[string]clustermanager.AdminState
 		ResourceStatusVSAddressMap  map[resourceRef]string
 		respChan                    chan *agentPostConfig
+		allowedPartitions           map[string]struct{}
+		deniedPartitions            map[string]struct{}
 		resourceContext
 	}
 	resourceContext struct {
@@ -222,6 +224,8 @@ type (
 		IpamNamespace               string
 		LocalClusterName            string
 		CustomResourceLabel         string
+		AllowedPartitions           []string
+		DeniedPartitions            []string
 	}
 
 	// CRInformer defines the structure of Custom Resource Informer
@@ -316,6 +320,7 @@ type (
 		WAF                        string                `json:"waf,omitempty"`
 		Firewall                   string                `json:"firewallPolicy,omitempty"`
 		LogProfiles                []string              `json:"logProfiles,omitempty"`
+		RequestLogProfile          string                `json:"requestLogProfile,omitempty"`
 		ProfileL4                  string                `json:"profileL4,omitempty"`
 		ProfileMultiplex           string                `json:"profileMultiplex,omitempty"`
 		HTTPCompressionProfile     string                `json:"profileHTTPCompression,omitempty"`
@@ -1208,6 +1213,7 @@ type (
 		WAF                    as3MultiTypeParam    `json:"policyWAF,omitempty"`
 		Firewall               as3MultiTypeParam    `json:"policyFirewallEnforced,omitempty"`
 		LogProfiles            []as3ResourcePointer `json:"securityLogProfiles,omitempty"`
+		ProfileTrafficLog      as3MultiTypeParam    `json:"profileTrafficLog,omitempty"`
 		ProfileL4              as3MultiTypeParam    `json:"profileL4,omitempty"`
 		AllowVLANs             []as3ResourcePointer `json:"allowVlans,omitempty"`
 		PersistenceMethods     *[]as3MultiTypeParam `json:"persistenceMethods,omitempty"`
diff --git a/pkg/controller/validate.go b/pkg/controller/validate.go
index 7cdfe4d5..c02579c5 100644
--- a/pkg/controller/validate.go
+++ b/pkg/controller/validate.go
@@ -59,6 +59,10 @@ func (ctlr *Controller) checkValidVirtualServer(
 		return false
 	}
 
+	if !ctlr.validateVSPartitionAccess(vsResource, false) {
+		return false
+	}
+
 	// Check if HTTPTraffic is set for insecure VS
 	if vsResource.Spec.TLSProfileName == "" && vsResource.Spec.HTTPTraffic != "" {
 		err = fmt.Sprintf("HTTPTraffic not allowed to be set for insecure VirtualServer: %v", vsName)
@@ -375,3 +379,55 @@ func getL4AppConfigForService(svc *v1.Service, ipamClusterLabel string, routeDom
 	}
 	return l4AppConfigs
 }
+
+// getKeysFromSet returns a comma-separated string of keys from a map used as a set(map[string]struct{})
+func getKeysFromSet(m map[string]struct{}) string {
+	keys := make([]string, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	return strings.Join(keys, ",")
+}
+
+// validateVSPartitionAccess checks if the partition of the VirtualServer is allowed or denied and
+// updates the status accordingly if needed (only for non-delete operations)
+func (ctlr *Controller) validateVSPartitionAccess(vsResource *cisapiv1.VirtualServer, deleted bool) bool {
+	// If vsResource is nil, return false
+	// This is a safety check, in practice this should not happen
+	if vsResource == nil {
+		return false
+	}
+	// If both allowed and denied partitions are empty, allow all partitions
+	partition := vsResource.Spec.Partition
+	if partition == "" {
+		return true
+	}
+	vsName := vsResource.ObjectMeta.Name
+	// Check if Partition is present in allowed/denied list
+	// Denied partition list takes precedence over allowed list
+	if len(ctlr.deniedPartitions) > 0 {
+		if _, ok := ctlr.deniedPartitions[partition]; ok {
+			// If operation is delete, simply return false without updating status, otherwise update status
+			if !deleted {
+				err := fmt.Sprintf("VirtualServer %s cannot be created in partition %s. Denied partitions are: %s",
+					vsName, partition, getKeysFromSet(ctlr.deniedPartitions))
+				log.Errorf(err)
+				ctlr.updateVSStatus(vsResource, "", StatusError, errors.New(err))
+			}
+			return false
+		}
+	} else if len(ctlr.allowedPartitions) > 0 {
+		if _, ok := ctlr.allowedPartitions[partition]; !ok {
+			// If operation is delete, simply return false without updating status, otherwise update status
+			if !deleted {
+				err := fmt.Sprintf("VirtualServer %s cannot be created in partition %s. Allowed partitions are: %s",
+					vsName, partition, getKeysFromSet(ctlr.allowedPartitions))
+				log.Errorf(err)
+				ctlr.updateVSStatus(vsResource, "", StatusError, errors.New(err))
+			}
+			return false
+		}
+	}
+	// If we reach here, partition is valid
+	return true
+}
diff --git a/pkg/controller/worker.go b/pkg/controller/worker.go
index d2f315c4..ff593fc0 100644
--- a/pkg/controller/worker.go
+++ b/pkg/controller/worker.go
@@ -1369,6 +1369,12 @@ func (ctlr *Controller) processVirtualServers(
 				vkey)
 			return nil
 		}
+	} else if !ctlr.validateVSPartitionAccess(virtual, true) {
+		// If partition is not allowed, skip processing the VS deletion request
+		// This is to avoid deletion of VS in BIG-IP when partition is not allowed
+		log.Warningf("VirtualServer %s/%s: Skipping processing as partition %s is not allowed",
+			virtual.Namespace, virtual.Name, virtual.Spec.Partition)
+		return nil
 	}
 
 	var allVirtuals []*cisapiv1.VirtualServer



Test patch:
diff --git a/cmd/k8s-bigip-ctlr/main_test.go b/cmd/k8s-bigip-ctlr/main_test.go
index 5029dac4..d1a5d65f 100644
--- a/cmd/k8s-bigip-ctlr/main_test.go
+++ b/cmd/k8s-bigip-ctlr/main_test.go
@@ -1214,5 +1214,105 @@ var _ = Describe("Main Tests", func() {
 			Expect(err).To(BeNil())
 			Expect(getSDNType(config)).To(Equal("openshiftSDN"), "SDNType should be other")
 		})
+
+		It("processes partition lists correctly", func() {
+			defer _init()
+
+			// Test denied partitions
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--denied-partitions=partition1,partition2",
+			}
+			flags.Parse(os.Args)
+			err := verifyArgs()
+			Expect(err).To(BeNil())
+			Expect(deniedList).To(ConsistOf("partition1", "partition2"))
+			Expect(allowedList).To(BeNil())
+
+			// Test allowed partitions
+			_init() // Reset
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--allowed-partitions=partition1,partition2",
+			}
+			flags.Parse(os.Args)
+			err = verifyArgs()
+			Expect(err).To(BeNil())
+			Expect(allowedList).To(ContainElements("partition1", "partition2", "velcro1"))
+			Expect(deniedList).To(BeNil())
+
+			// Test denied takes precedence over allowed
+			_init() // Reset
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--denied-partitions=partition1,partition2",
+				"--allowed-partitions=partition3,partition4",
+			}
+			flags.Parse(os.Args)
+			err = verifyArgs()
+			Expect(err).To(BeNil())
+			Expect(deniedList).To(ConsistOf("partition1", "partition2"))
+			Expect(allowedList).To(BeNil())
+		})
+
+		It("validates partition error conditions", func() {
+			defer _init()
+
+			// Test error when default partition is in denied list
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--denied-partitions=velcro1,partition2",
+			}
+			flags.Parse(os.Args)
+			err := verifyArgs()
+			Expect(err).ToNot(BeNil())
+			Expect(err.Error()).To(ContainSubstring("Default Partition velcro1 cannot be specified in denied partitions list"))
+
+			// Test error when Common is in allowed list
+			_init() // Reset
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--allowed-partitions=partition1,Common",
+			}
+			flags.Parse(os.Args)
+			err = verifyArgs()
+			Expect(err).ToNot(BeNil())
+			Expect(err.Error()).To(ContainSubstring("Common cannot be specified in allowed partitions list"))
+
+			// Test with whitespace and quotes in partition names
+			_init() // Reset
+			os.Args = []string{
+				"./bin/k8s-bigip-ctlr",
+				"--bigip-partition=velcro1",
+				"--bigip-url=bigip.example.com",
+				"--bigip-username=admin",
+				"--bigip-password=admin",
+				"--denied-partitions= ' partition1 '  ,'   partition2'",
+			}
+			flags.Parse(os.Args)
+			err = verifyArgs()
+			Expect(err).To(BeNil())
+			Expect(deniedList).To(ConsistOf("partition1", "partition2"))
+		})
 	})
 })
diff --git a/pkg/controller/validate_test.go b/pkg/controller/validate_test.go
index 025ec0d1..97cc30e0 100644
--- a/pkg/controller/validate_test.go
+++ b/pkg/controller/validate_test.go
@@ -98,4 +98,119 @@ var _ = Describe("Validation Tests", func() {
 			//	"HA clusters to be defined in extendedServiceReference")))
 		})
 	})
+
+	Describe("getKeysFromSet", func() {
+		It("should return an empty string for an empty map", func() {
+			emptyMap := map[string]struct{}{}
+			Expect(getKeysFromSet(emptyMap)).To(Equal(""))
+		})
+
+		It("should return the key for a single-entry map", func() {
+			singleMap := map[string]struct{}{"partition1": {}}
+			Expect(getKeysFromSet(singleMap)).To(Equal("partition1"))
+		})
+
+		It("should return comma-separated keys for a multi-entry map", func() {
+			// Create map with multiple entries
+			multiMap := map[string]struct{}{
+				"partition1": {},
+				"partition2": {},
+				"partition3": {},
+			}
+
+			// Get the result
+			result := getKeysFromSet(multiMap)
+
+			// Since map iteration order is non-deterministic, we need to check all keys are present
+			Expect(result).To(ContainSubstring("partition1"))
+			Expect(result).To(ContainSubstring("partition2"))
+			Expect(result).To(ContainSubstring("partition3"))
+			Expect(result).To(HaveLen(len("partition1,partition2,partition3")))
+		})
+	})
+
+	Describe("validateVSPartitionAccess", func() {
+		var vsResource *cisapiv1.VirtualServer
+
+		BeforeEach(func() {
+			vsResource = &cisapiv1.VirtualServer{
+				Spec: cisapiv1.VirtualServerSpec{
+					Partition: "test-partition",
+				},
+			}
+			mockCtlr.deniedPartitions = make(map[string]struct{})
+			mockCtlr.allowedPartitions = make(map[string]struct{})
+		})
+
+		It("should return false for nil VirtualServer", func() {
+			Expect(mockCtlr.validateVSPartitionAccess(nil, false)).To(BeFalse())
+		})
+
+		It("should allow when partition is empty", func() {
+			vsResource.Spec.Partition = ""
+			Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeTrue())
+		})
+
+		It("should allow when no allowed/denied partitions configured", func() {
+			Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeTrue())
+		})
+
+		Context("with denied partitions", func() {
+			BeforeEach(func() {
+				mockCtlr.deniedPartitions = map[string]struct{}{
+					"denied-partition": {},
+				}
+			})
+
+			It("should deny when partition is in denied list", func() {
+				vsResource.Spec.Partition = "denied-partition"
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeFalse())
+			})
+
+			It("should allow when partition is not in denied list", func() {
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeTrue())
+			})
+
+			It("should not update status when deleted flag is true", func() {
+				vsResource.Spec.Partition = "denied-partition"
+				// We can't directly test status updates, but we can verify the function behavior
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, true)).To(BeFalse())
+			})
+		})
+
+		Context("with allowed partitions", func() {
+			BeforeEach(func() {
+				mockCtlr.allowedPartitions = map[string]struct{}{
+					"allowed-partition": {},
+				}
+			})
+
+			It("should allow when partition is in allowed list", func() {
+				vsResource.Spec.Partition = "allowed-partition"
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeTrue())
+			})
+
+			It("should deny when partition is not in allowed list", func() {
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeFalse())
+			})
+		})
+
+		Context("with both allowed and denied partitions", func() {
+			BeforeEach(func() {
+				mockCtlr.deniedPartitions = map[string]struct{}{
+					"denied-partition": {},
+					"common-partition": {},
+				}
+				mockCtlr.allowedPartitions = map[string]struct{}{
+					"allowed-partition": {},
+					"common-partition":  {},
+				}
+			})
+
+			It("should prioritize denied over allowed partitions", func() {
+				vsResource.Spec.Partition = "common-partition"
+				Expect(mockCtlr.validateVSPartitionAccess(vsResource, false)).To(BeFalse())
+			})
+		})
+	})
 })