1+ import re
12import warnings
23from pydantic_settings import (
34 BaseSettings ,
1819 model_validator ,
1920)
2021from pydantic .networks import IPvAnyAddress
21- from typing import Annotated , Literal
22+ from typing import Annotated , Final , Literal
2223import consts
2324from utils .logs import LoggingFormats
2425from datetime import timedelta
2526
27+ NAME_REGEX : Final [re .Pattern ] = re .compile (
28+ r"^(?=.{1,253}$)(?!.*\.\.)([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)(\.([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?))*$"
29+ )
30+
31+
32+ class StoragePool (BaseModel ):
33+ path : DirectoryPath = Field (description = "Path of the pool, Should be unique" )
34+ reserved_capacity : (
35+ ByteSize
36+ | Annotated [
37+ str ,
38+ StringConstraints (strip_whitespace = True , pattern = r"^\d+%$" ),
39+ ]
40+ ) = Field (
41+ default = ByteSize (0 ),
42+ description = "Reserves capacity of storage pool" ,
43+ )
44+ capacity_override : ByteSize | None = Field (
45+ default = None ,
46+ description = "Overrides total capacity of storage pool" ,
47+ )
48+ default_fs : FileSystemName = Field (
49+ default = FileSystemName .EXT4 ,
50+ description = "Default filesystem used where creating volumes and fsType is not specified in storage class parameters" ,
51+ )
52+
2653
2754class CSIDriverCmd (BaseModel ):
2855 endpoint : (
@@ -33,6 +60,14 @@ class CSIDriverCmd(BaseModel):
3360 ) = Field (
3461 description = "Listen address for gRPC server" ,
3562 )
63+ storage_pools : dict [str , StoragePool ] | None = Field (
64+ description = "List of storage pools (Map of name to configuration), required when running node plugin" ,
65+ default = None ,
66+ )
67+ default_pool : str | None = Field (
68+ description = "Name of the default storage pool, used when no storage pool is defined in storage class" ,
69+ default = None ,
70+ )
3671 internal_ip : IPvAnyAddress | None = Field (
3772 description = "Listen ip for gRPC server (used for internal communication only)" ,
3873 default = None ,
@@ -77,13 +112,31 @@ class CSIDriverCmd(BaseModel):
77112 def validate_node_plugin (
78113 self ,
79114 ):
80- if self .plugin_type = = "node" :
115+ if self .plugin_type ! = "node" :
81116 if not self .internal_ip :
82117 raise ValueError (
83118 "Internal Communication IP/PORT is required on node plugin"
84119 )
85120 if not self .metadata_dir :
86121 raise ValueError ("Metadata Dir is required when running node plugin" )
122+ if not self .storage_pools or len (self .storage_pools ) == 0 :
123+ raise ValueError (
124+ "Storage Pool list is required when running node plugin"
125+ )
126+
127+ paths = []
128+ for name , pool in self .storage_pools .items ():
129+ if len (name ) > 63 or len (name ) < 3 :
130+ raise ValueError (
131+ "Name of the storage pool should be between 3 and 63 characters"
132+ )
133+ if not NAME_REGEX .match (name ):
134+ raise ValueError (
135+ "Name of the storage pool should be DNS compatible"
136+ )
137+ if pool .path in paths :
138+ raise ValueError ("Duplicate path in storage pool is not supported" )
139+ paths .append (pool .path )
87140 return self
88141
89142
@@ -110,24 +163,6 @@ def settings_customise_sources(
110163 ) -> tuple [PydanticBaseSettingsSource , ...]:
111164 return CliSettingsSource (settings_cls , cli_parse_args = True ), env_settings
112165
113- reserved_capacity : (
114- ByteSize
115- | Annotated [
116- str ,
117- StringConstraints (strip_whitespace = True , pattern = r"^\d+%$" ),
118- ]
119- ) = Field (
120- default = ByteSize (0 ),
121- description = "Reserves capacity of data dir" ,
122- )
123- capacity_override : ByteSize | None = Field (
124- default = None ,
125- description = "Overrides total capacity of data dir" ,
126- )
127- default_fs : FileSystemName = Field (
128- default = FileSystemName .EXT4 ,
129- description = "Default filesystem used where creating volumes and fsType is not specified in storage class parameters" ,
130- )
131166 namespace : str = Field (
132167 description = "K8s Namespace of the driver" ,
133168 )
0 commit comments