We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
RDS Validiation: minimum allocated storage for Provisioned IOPS for SQLServer is 20GiB, not 100GiB
Troposphere validates the minimum allocated storage required for provisioned iops at 100
https://github.com/cloudtools/troposphere/blob/main/troposphere/validators/rds.py#L345

For SQLServer, the minimum is 20
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#USER_PIOPS

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `troposphere/validators/rds.py`
Content:
```
1 # Copyright (c) 2012-2022, Mark Peek <mark@peek.org>
2 # All rights reserved.
3 #
4 # See LICENSE file for full license.
5 
6 
7 import re
8 
9 from .. import AWSHelperFn
10 from . import integer, integer_range, network_port, positive_integer, tags_or_list
11 
12 
13 def validate_network_port(x):
14     """
15     Property: DBCluster.Port
16     Property: DBInstance.Port
17     Property: OptionConfiguration.Port
18     """
19     return network_port(x)
20 
21 
22 def validate_str_or_int(x):
23     """
24     Property: DBInstance.AllocatedStorage
25     """
26     if isinstance(x, (AWSHelperFn, str, int)):
27         return x
28     raise ValueError(f"Value {x} of type {type(x)} must be either str or int")
29 
30 
31 def validate_tags_or_list(x):
32     """
33     Property: DBCluster.Tags
34     Property: DBClusterParameterGroup.Tags
35     Property: DBInstance.Tags
36     Property: DBParameterGroup.Tags
37     Property: DBSecurityGroup.Tags
38     Property: DBSubnetGroup.Tags
39     Property: OptionGroup.Tags
40     """
41     return tags_or_list(x)
42 
43 
44 def validate_backtrack_window(x):
45     """
46     Property: DBCluster.BacktrackWindow
47     """
48     return integer_range(0, 259200)(x)
49 
50 
51 def validate_iops(iops):
52     """
53     DBInstance Iops validation rules.
54     Property: DBInstance.Iops
55     """
56 
57     iops = integer(iops)
58     if int(iops) == 0:
59         return iops
60     if int(iops) < 1000:
61         raise ValueError("DBInstance Iops, if set, must be greater than 1000.")
62     return iops
63 
64 
65 def validate_storage_type(storage_type):
66     """
67     Validate StorageType for DBInstance
68     Property:
69     """
70 
71     VALID_STORAGE_TYPES = ("standard", "gp2", "io1")
72 
73     if storage_type not in VALID_STORAGE_TYPES:
74         raise ValueError(
75             "DBInstance StorageType must be one of: %s" % ", ".join(VALID_STORAGE_TYPES)
76         )
77     return storage_type
78 
79 
80 def validate_engine(engine):
81     """
82     Validate database Engine for DBInstance
83     Property: DBInstance.Engine
84     Property: DBCluster.Engine
85     """
86 
87     VALID_DB_ENGINES = (
88         "MySQL",
89         "mysql",
90         "oracle-se1",
91         "oracle-se2",
92         "oracle-se",
93         "oracle-ee",
94         "sqlserver-ee",
95         "sqlserver-se",
96         "sqlserver-ex",
97         "sqlserver-web",
98         "postgres",
99         "aurora",
100         "aurora-mysql",
101         "aurora-postgresql",
102         "mariadb",
103     )
104 
105     if engine not in VALID_DB_ENGINES:
106         raise ValueError(
107             "DBInstance Engine must be one of: %s" % ", ".join(VALID_DB_ENGINES)
108         )
109     return engine
110 
111 
112 def validate_engine_mode(engine_mode):
113     """
114     Validate database EngineMode for DBCluster
115     Property: DBCluster.EngineMode
116     """
117 
118     VALID_DB_ENGINE_MODES = (
119         "provisioned",
120         "serverless",
121         "parallelquery",
122         "global",
123         "multimaster",
124     )
125 
126     if engine_mode not in VALID_DB_ENGINE_MODES:
127         raise ValueError(
128             "DBCluster EngineMode must be one of: %s" % ", ".join(VALID_DB_ENGINE_MODES)
129         )
130     return engine_mode
131 
132 
133 def validate_license_model(license_model):
134     """
135     Validate LicenseModel for DBInstance
136     Property: DBInstance.LicenseModel
137     """
138 
139     VALID_LICENSE_MODELS = (
140         "license-included",
141         "bring-your-own-license",
142         "general-public-license",
143         "postgresql-license",
144     )
145 
146     if license_model not in VALID_LICENSE_MODELS:
147         raise ValueError(
148             "DBInstance LicenseModel must be one of: %s"
149             % ", ".join(VALID_LICENSE_MODELS)
150         )
151     return license_model
152 
153 
154 def validate_backup_window(window):
155     """
156     Validate PreferredBackupWindow for DBInstance
157     Property: DBInstance.PreferredBackupWindow
158     Property: DBCluster.PreferredBackupWindow
159     """
160 
161     hour = r"[01]?[0-9]|2[0-3]"
162     minute = r"[0-5][0-9]"
163     r = (
164         "(?P<start_hour>%s):(?P<start_minute>%s)-" "(?P<end_hour>%s):(?P<end_minute>%s)"
165     ) % (hour, minute, hour, minute)
166     range_regex = re.compile(r)
167     m = range_regex.match(window)
168     if not m:
169         raise ValueError(
170             "DBInstance PreferredBackupWindow must be in the " "format: hh24:mi-hh24:mi"
171         )
172     start_ts = (int(m.group("start_hour")) * 60) + int(m.group("start_minute"))
173     end_ts = (int(m.group("end_hour")) * 60) + int(m.group("end_minute"))
174     if abs(end_ts - start_ts) < 30:
175         raise ValueError(
176             "DBInstance PreferredBackupWindow must be at least " "30 minutes long."
177         )
178     return window
179 
180 
181 def validate_maintenance_window(window):
182     """
183     Validate PreferredMaintenanceWindow for DBInstance
184     """
185 
186     days = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
187     day_re = r"[A-Z]{1}[a-z]{2}"
188     hour = r"[01]?[0-9]|2[0-3]"
189     minute = r"[0-5][0-9]"
190     r = (
191         "(?P<start_day>%s):(?P<start_hour>%s):(?P<start_minute>%s)-"
192         "(?P<end_day>%s):(?P<end_hour>%s):(?P<end_minute>%s)"
193     ) % (day_re, hour, minute, day_re, hour, minute)
194     range_regex = re.compile(r)
195     m = range_regex.match(window)
196     if not m:
197         raise ValueError(
198             "DBInstance PreferredMaintenanceWindow must be in "
199             "the format: ddd:hh24:mi-ddd:hh24:mi"
200         )
201     if m.group("start_day") not in days or m.group("end_day") not in days:
202         raise ValueError(
203             "DBInstance PreferredMaintenanceWindow day part of "
204             "ranges must be one of: %s" % ", ".join(days)
205         )
206     start_ts = (
207         (days.index(m.group("start_day")) * 24 * 60)
208         + (int(m.group("start_hour")) * 60)
209         + int(m.group("start_minute"))
210     )
211     end_ts = (
212         (days.index(m.group("end_day")) * 24 * 60)
213         + (int(m.group("end_hour")) * 60)
214         + int(m.group("end_minute"))
215     )
216     if abs(end_ts - start_ts) < 30:
217         raise ValueError(
218             "DBInstance PreferredMaintenanceWindow must be at " "least 30 minutes long."
219         )
220     return window
221 
222 
223 def validate_backup_retention_period(days):
224     """
225     Validate BackupRetentionPeriod for DBInstance
226     Property: DBInstance.BackupRetentionPeriod
227     Property: DBCluster.BackupRetentionPeriod
228     """
229 
230     days = positive_integer(days)
231     if int(days) > 35:
232         raise ValueError(
233             "DBInstance BackupRetentionPeriod cannot be larger " "than 35 days."
234         )
235     return days
236 
237 
238 def validate_capacity(capacity):
239     """
240     Validate ScalingConfiguration capacity for serverless DBCluster
241     Property: ScalingConfiguration.MaxCapacity
242     Property: ScalingConfiguration.MinCapacity
243     """
244 
245     VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES = (1, 2, 4, 8, 16, 32, 64, 128, 256)
246     VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES = (2, 4, 8, 16, 32, 64, 192, 384)
247     if (
248         capacity not in VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES
249         and capacity not in VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES
250     ):
251         raise ValueError(
252             "ScalingConfiguration capacity must be one of: {}".format(
253                 ", ".join(
254                     map(
255                         str,
256                         VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES
257                         + VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES,
258                     )
259                 )
260             )
261         )
262     return capacity
263 
264 
265 def validate_dbinstance(self) -> None:
266     """
267     Class: DBInstance
268     """
269     if "DBSnapshotIdentifier" not in self.properties:
270         if "Engine" not in self.properties:
271             raise ValueError(
272                 "Resource Engine is required in type %s" % self.resource_type
273             )
274 
275     if "SourceDBInstanceIdentifier" in self.properties:
276 
277         invalid_replica_properties = (
278             "BackupRetentionPeriod",
279             "DBName",
280             "MasterUsername",
281             "MasterUserPassword",
282             "PreferredBackupWindow",
283             "MultiAZ",
284             "DBSnapshotIdentifier",
285         )
286 
287         invalid_properties = [
288             s for s in self.properties.keys() if s in invalid_replica_properties
289         ]
290 
291         if invalid_properties:
292             raise ValueError(
293                 (
294                     "{0} properties can't be provided when "
295                     "SourceDBInstanceIdentifier is present "
296                     "AWS::RDS::DBInstance."
297                 ).format(", ".join(sorted(invalid_properties)))
298             )
299 
300     if (
301         (
302             "DBSnapshotIdentifier" not in self.properties
303             and "SourceDBInstanceIdentifier" not in self.properties
304         )
305         and (
306             "MasterUsername" not in self.properties
307             or "MasterUserPassword" not in self.properties
308         )
309         and ("DBClusterIdentifier" not in self.properties)
310     ):
311         raise ValueError(
312             r"Either (MasterUsername and MasterUserPassword) or"
313             r" DBSnapshotIdentifier are required in type "
314             r"AWS::RDS::DBInstance."
315         )
316 
317     if "KmsKeyId" in self.properties and "StorageEncrypted" not in self.properties:
318         raise ValueError(
319             "If KmsKeyId is provided, StorageEncrypted is required "
320             "AWS::RDS::DBInstance."
321         )
322 
323     nonetype = type(None)
324     avail_zone = self.properties.get("AvailabilityZone", None)
325     multi_az = self.properties.get("MultiAZ", None)
326     if not (
327         isinstance(avail_zone, (AWSHelperFn, nonetype))
328         and isinstance(multi_az, (AWSHelperFn, nonetype))
329     ):
330         if avail_zone and multi_az in [True, 1, "1", "true", "True"]:
331             raise ValueError(
332                 "AvailabiltyZone cannot be set on "
333                 "DBInstance if MultiAZ is set to true."
334             )
335 
336     storage_type = self.properties.get("StorageType", None)
337     if storage_type and storage_type == "io1" and "Iops" not in self.properties:
338         raise ValueError("Must specify Iops if using StorageType io1")
339 
340     allocated_storage = self.properties.get("AllocatedStorage")
341     iops = self.properties.get("Iops", None)
342     if iops and not isinstance(iops, AWSHelperFn):
343         if (
344             not isinstance(allocated_storage, AWSHelperFn)
345             and int(allocated_storage) < 100
346         ):
347             raise ValueError(
348                 "AllocatedStorage must be at least 100 when " "Iops is set."
349             )
350         if (
351             not isinstance(allocated_storage, AWSHelperFn)
352             and not isinstance(iops, AWSHelperFn)
353             and float(iops) / float(allocated_storage) > 50.0
354         ):
355             raise ValueError(
356                 "AllocatedStorage must be no less than " "1/50th the provisioned Iops"
357             )
358
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/troposphere/validators/rds.py b/troposphere/validators/rds.py
--- a/troposphere/validators/rds.py
+++ b/troposphere/validators/rds.py
@@ -340,12 +340,18 @@
     allocated_storage = self.properties.get("AllocatedStorage")
     iops = self.properties.get("Iops", None)
     if iops and not isinstance(iops, AWSHelperFn):
+        min_storage_size = 100
+        engine = self.properties.get("Engine")
+        if not isinstance(engine, AWSHelperFn) and engine.startswith("sqlserver"):
+            min_storage_size = 20
+
         if (
             not isinstance(allocated_storage, AWSHelperFn)
-            and int(allocated_storage) < 100
+            and int(allocated_storage) < min_storage_size
         ):
             raise ValueError(
-                "AllocatedStorage must be at least 100 when " "Iops is set."
+                f"AllocatedStorage must be at least {min_storage_size} when "
+                "Iops is set."
             )
         if (
             not isinstance(allocated_storage, AWSHelperFn)

{"golden_diff": "diff --git a/troposphere/validators/rds.py b/troposphere/validators/rds.py\n--- a/troposphere/validators/rds.py\n+++ b/troposphere/validators/rds.py\n@@ -340,12 +340,18 @@\n     allocated_storage = self.properties.get(\"AllocatedStorage\")\n     iops = self.properties.get(\"Iops\", None)\n     if iops and not isinstance(iops, AWSHelperFn):\n+        min_storage_size = 100\n+        engine = self.properties.get(\"Engine\")\n+        if not isinstance(engine, AWSHelperFn) and engine.startswith(\"sqlserver\"):\n+            min_storage_size = 20\n+\n         if (\n             not isinstance(allocated_storage, AWSHelperFn)\n-            and int(allocated_storage) < 100\n+            and int(allocated_storage) < min_storage_size\n         ):\n             raise ValueError(\n-                \"AllocatedStorage must be at least 100 when \" \"Iops is set.\"\n+                f\"AllocatedStorage must be at least {min_storage_size} when \"\n+                \"Iops is set.\"\n             )\n         if (\n             not isinstance(allocated_storage, AWSHelperFn)\n", "issue": "RDS Validiation: minimum allocated storage for Provisioned IOPS for SQLServer is 20GiB, not 100GiB\nTroposphere validates the minimum allocated storage required for provisioned iops at 100\r\nhttps://github.com/cloudtools/troposphere/blob/main/troposphere/validators/rds.py#L345\r\n\r\nFor SQLServer, the minimum is 20\r\nhttps://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html#USER_PIOPS\n", "before_files": [{"content": "# Copyright (c) 2012-2022, Mark Peek <mark@peek.org>\n# All rights reserved.\n#\n# See LICENSE file for full license.\n\n\nimport re\n\nfrom .. import AWSHelperFn\nfrom . import integer, integer_range, network_port, positive_integer, tags_or_list\n\n\ndef validate_network_port(x):\n    \"\"\"\n    Property: DBCluster.Port\n    Property: DBInstance.Port\n    Property: OptionConfiguration.Port\n    \"\"\"\n    return network_port(x)\n\n\ndef validate_str_or_int(x):\n    \"\"\"\n    Property: DBInstance.AllocatedStorage\n    \"\"\"\n    if isinstance(x, (AWSHelperFn, str, int)):\n        return x\n    raise ValueError(f\"Value {x} of type {type(x)} must be either str or int\")\n\n\ndef validate_tags_or_list(x):\n    \"\"\"\n    Property: DBCluster.Tags\n    Property: DBClusterParameterGroup.Tags\n    Property: DBInstance.Tags\n    Property: DBParameterGroup.Tags\n    Property: DBSecurityGroup.Tags\n    Property: DBSubnetGroup.Tags\n    Property: OptionGroup.Tags\n    \"\"\"\n    return tags_or_list(x)\n\n\ndef validate_backtrack_window(x):\n    \"\"\"\n    Property: DBCluster.BacktrackWindow\n    \"\"\"\n    return integer_range(0, 259200)(x)\n\n\ndef validate_iops(iops):\n    \"\"\"\n    DBInstance Iops validation rules.\n    Property: DBInstance.Iops\n    \"\"\"\n\n    iops = integer(iops)\n    if int(iops) == 0:\n        return iops\n    if int(iops) < 1000:\n        raise ValueError(\"DBInstance Iops, if set, must be greater than 1000.\")\n    return iops\n\n\ndef validate_storage_type(storage_type):\n    \"\"\"\n    Validate StorageType for DBInstance\n    Property:\n    \"\"\"\n\n    VALID_STORAGE_TYPES = (\"standard\", \"gp2\", \"io1\")\n\n    if storage_type not in VALID_STORAGE_TYPES:\n        raise ValueError(\n            \"DBInstance StorageType must be one of: %s\" % \", \".join(VALID_STORAGE_TYPES)\n        )\n    return storage_type\n\n\ndef validate_engine(engine):\n    \"\"\"\n    Validate database Engine for DBInstance\n    Property: DBInstance.Engine\n    Property: DBCluster.Engine\n    \"\"\"\n\n    VALID_DB_ENGINES = (\n        \"MySQL\",\n        \"mysql\",\n        \"oracle-se1\",\n        \"oracle-se2\",\n        \"oracle-se\",\n        \"oracle-ee\",\n        \"sqlserver-ee\",\n        \"sqlserver-se\",\n        \"sqlserver-ex\",\n        \"sqlserver-web\",\n        \"postgres\",\n        \"aurora\",\n        \"aurora-mysql\",\n        \"aurora-postgresql\",\n        \"mariadb\",\n    )\n\n    if engine not in VALID_DB_ENGINES:\n        raise ValueError(\n            \"DBInstance Engine must be one of: %s\" % \", \".join(VALID_DB_ENGINES)\n        )\n    return engine\n\n\ndef validate_engine_mode(engine_mode):\n    \"\"\"\n    Validate database EngineMode for DBCluster\n    Property: DBCluster.EngineMode\n    \"\"\"\n\n    VALID_DB_ENGINE_MODES = (\n        \"provisioned\",\n        \"serverless\",\n        \"parallelquery\",\n        \"global\",\n        \"multimaster\",\n    )\n\n    if engine_mode not in VALID_DB_ENGINE_MODES:\n        raise ValueError(\n            \"DBCluster EngineMode must be one of: %s\" % \", \".join(VALID_DB_ENGINE_MODES)\n        )\n    return engine_mode\n\n\ndef validate_license_model(license_model):\n    \"\"\"\n    Validate LicenseModel for DBInstance\n    Property: DBInstance.LicenseModel\n    \"\"\"\n\n    VALID_LICENSE_MODELS = (\n        \"license-included\",\n        \"bring-your-own-license\",\n        \"general-public-license\",\n        \"postgresql-license\",\n    )\n\n    if license_model not in VALID_LICENSE_MODELS:\n        raise ValueError(\n            \"DBInstance LicenseModel must be one of: %s\"\n            % \", \".join(VALID_LICENSE_MODELS)\n        )\n    return license_model\n\n\ndef validate_backup_window(window):\n    \"\"\"\n    Validate PreferredBackupWindow for DBInstance\n    Property: DBInstance.PreferredBackupWindow\n    Property: DBCluster.PreferredBackupWindow\n    \"\"\"\n\n    hour = r\"[01]?[0-9]|2[0-3]\"\n    minute = r\"[0-5][0-9]\"\n    r = (\n        \"(?P<start_hour>%s):(?P<start_minute>%s)-\" \"(?P<end_hour>%s):(?P<end_minute>%s)\"\n    ) % (hour, minute, hour, minute)\n    range_regex = re.compile(r)\n    m = range_regex.match(window)\n    if not m:\n        raise ValueError(\n            \"DBInstance PreferredBackupWindow must be in the \" \"format: hh24:mi-hh24:mi\"\n        )\n    start_ts = (int(m.group(\"start_hour\")) * 60) + int(m.group(\"start_minute\"))\n    end_ts = (int(m.group(\"end_hour\")) * 60) + int(m.group(\"end_minute\"))\n    if abs(end_ts - start_ts) < 30:\n        raise ValueError(\n            \"DBInstance PreferredBackupWindow must be at least \" \"30 minutes long.\"\n        )\n    return window\n\n\ndef validate_maintenance_window(window):\n    \"\"\"\n    Validate PreferredMaintenanceWindow for DBInstance\n    \"\"\"\n\n    days = (\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\")\n    day_re = r\"[A-Z]{1}[a-z]{2}\"\n    hour = r\"[01]?[0-9]|2[0-3]\"\n    minute = r\"[0-5][0-9]\"\n    r = (\n        \"(?P<start_day>%s):(?P<start_hour>%s):(?P<start_minute>%s)-\"\n        \"(?P<end_day>%s):(?P<end_hour>%s):(?P<end_minute>%s)\"\n    ) % (day_re, hour, minute, day_re, hour, minute)\n    range_regex = re.compile(r)\n    m = range_regex.match(window)\n    if not m:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow must be in \"\n            \"the format: ddd:hh24:mi-ddd:hh24:mi\"\n        )\n    if m.group(\"start_day\") not in days or m.group(\"end_day\") not in days:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow day part of \"\n            \"ranges must be one of: %s\" % \", \".join(days)\n        )\n    start_ts = (\n        (days.index(m.group(\"start_day\")) * 24 * 60)\n        + (int(m.group(\"start_hour\")) * 60)\n        + int(m.group(\"start_minute\"))\n    )\n    end_ts = (\n        (days.index(m.group(\"end_day\")) * 24 * 60)\n        + (int(m.group(\"end_hour\")) * 60)\n        + int(m.group(\"end_minute\"))\n    )\n    if abs(end_ts - start_ts) < 30:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow must be at \" \"least 30 minutes long.\"\n        )\n    return window\n\n\ndef validate_backup_retention_period(days):\n    \"\"\"\n    Validate BackupRetentionPeriod for DBInstance\n    Property: DBInstance.BackupRetentionPeriod\n    Property: DBCluster.BackupRetentionPeriod\n    \"\"\"\n\n    days = positive_integer(days)\n    if int(days) > 35:\n        raise ValueError(\n            \"DBInstance BackupRetentionPeriod cannot be larger \" \"than 35 days.\"\n        )\n    return days\n\n\ndef validate_capacity(capacity):\n    \"\"\"\n    Validate ScalingConfiguration capacity for serverless DBCluster\n    Property: ScalingConfiguration.MaxCapacity\n    Property: ScalingConfiguration.MinCapacity\n    \"\"\"\n\n    VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES = (1, 2, 4, 8, 16, 32, 64, 128, 256)\n    VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES = (2, 4, 8, 16, 32, 64, 192, 384)\n    if (\n        capacity not in VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES\n        and capacity not in VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES\n    ):\n        raise ValueError(\n            \"ScalingConfiguration capacity must be one of: {}\".format(\n                \", \".join(\n                    map(\n                        str,\n                        VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES\n                        + VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES,\n                    )\n                )\n            )\n        )\n    return capacity\n\n\ndef validate_dbinstance(self) -> None:\n    \"\"\"\n    Class: DBInstance\n    \"\"\"\n    if \"DBSnapshotIdentifier\" not in self.properties:\n        if \"Engine\" not in self.properties:\n            raise ValueError(\n                \"Resource Engine is required in type %s\" % self.resource_type\n            )\n\n    if \"SourceDBInstanceIdentifier\" in self.properties:\n\n        invalid_replica_properties = (\n            \"BackupRetentionPeriod\",\n            \"DBName\",\n            \"MasterUsername\",\n            \"MasterUserPassword\",\n            \"PreferredBackupWindow\",\n            \"MultiAZ\",\n            \"DBSnapshotIdentifier\",\n        )\n\n        invalid_properties = [\n            s for s in self.properties.keys() if s in invalid_replica_properties\n        ]\n\n        if invalid_properties:\n            raise ValueError(\n                (\n                    \"{0} properties can't be provided when \"\n                    \"SourceDBInstanceIdentifier is present \"\n                    \"AWS::RDS::DBInstance.\"\n                ).format(\", \".join(sorted(invalid_properties)))\n            )\n\n    if (\n        (\n            \"DBSnapshotIdentifier\" not in self.properties\n            and \"SourceDBInstanceIdentifier\" not in self.properties\n        )\n        and (\n            \"MasterUsername\" not in self.properties\n            or \"MasterUserPassword\" not in self.properties\n        )\n        and (\"DBClusterIdentifier\" not in self.properties)\n    ):\n        raise ValueError(\n            r\"Either (MasterUsername and MasterUserPassword) or\"\n            r\" DBSnapshotIdentifier are required in type \"\n            r\"AWS::RDS::DBInstance.\"\n        )\n\n    if \"KmsKeyId\" in self.properties and \"StorageEncrypted\" not in self.properties:\n        raise ValueError(\n            \"If KmsKeyId is provided, StorageEncrypted is required \"\n            \"AWS::RDS::DBInstance.\"\n        )\n\n    nonetype = type(None)\n    avail_zone = self.properties.get(\"AvailabilityZone\", None)\n    multi_az = self.properties.get(\"MultiAZ\", None)\n    if not (\n        isinstance(avail_zone, (AWSHelperFn, nonetype))\n        and isinstance(multi_az, (AWSHelperFn, nonetype))\n    ):\n        if avail_zone and multi_az in [True, 1, \"1\", \"true\", \"True\"]:\n            raise ValueError(\n                \"AvailabiltyZone cannot be set on \"\n                \"DBInstance if MultiAZ is set to true.\"\n            )\n\n    storage_type = self.properties.get(\"StorageType\", None)\n    if storage_type and storage_type == \"io1\" and \"Iops\" not in self.properties:\n        raise ValueError(\"Must specify Iops if using StorageType io1\")\n\n    allocated_storage = self.properties.get(\"AllocatedStorage\")\n    iops = self.properties.get(\"Iops\", None)\n    if iops and not isinstance(iops, AWSHelperFn):\n        if (\n            not isinstance(allocated_storage, AWSHelperFn)\n            and int(allocated_storage) < 100\n        ):\n            raise ValueError(\n                \"AllocatedStorage must be at least 100 when \" \"Iops is set.\"\n            )\n        if (\n            not isinstance(allocated_storage, AWSHelperFn)\n            and not isinstance(iops, AWSHelperFn)\n            and float(iops) / float(allocated_storage) > 50.0\n        ):\n            raise ValueError(\n                \"AllocatedStorage must be no less than \" \"1/50th the provisioned Iops\"\n            )\n", "path": "troposphere/validators/rds.py"}], "after_files": [{"content": "# Copyright (c) 2012-2022, Mark Peek <mark@peek.org>\n# All rights reserved.\n#\n# See LICENSE file for full license.\n\n\nimport re\n\nfrom .. import AWSHelperFn\nfrom . import integer, integer_range, network_port, positive_integer, tags_or_list\n\n\ndef validate_network_port(x):\n    \"\"\"\n    Property: DBCluster.Port\n    Property: DBInstance.Port\n    Property: OptionConfiguration.Port\n    \"\"\"\n    return network_port(x)\n\n\ndef validate_str_or_int(x):\n    \"\"\"\n    Property: DBInstance.AllocatedStorage\n    \"\"\"\n    if isinstance(x, (AWSHelperFn, str, int)):\n        return x\n    raise ValueError(f\"Value {x} of type {type(x)} must be either str or int\")\n\n\ndef validate_tags_or_list(x):\n    \"\"\"\n    Property: DBCluster.Tags\n    Property: DBClusterParameterGroup.Tags\n    Property: DBInstance.Tags\n    Property: DBParameterGroup.Tags\n    Property: DBSecurityGroup.Tags\n    Property: DBSubnetGroup.Tags\n    Property: OptionGroup.Tags\n    \"\"\"\n    return tags_or_list(x)\n\n\ndef validate_backtrack_window(x):\n    \"\"\"\n    Property: DBCluster.BacktrackWindow\n    \"\"\"\n    return integer_range(0, 259200)(x)\n\n\ndef validate_iops(iops):\n    \"\"\"\n    DBInstance Iops validation rules.\n    Property: DBInstance.Iops\n    \"\"\"\n\n    iops = integer(iops)\n    if int(iops) == 0:\n        return iops\n    if int(iops) < 1000:\n        raise ValueError(\"DBInstance Iops, if set, must be greater than 1000.\")\n    return iops\n\n\ndef validate_storage_type(storage_type):\n    \"\"\"\n    Validate StorageType for DBInstance\n    Property:\n    \"\"\"\n\n    VALID_STORAGE_TYPES = (\"standard\", \"gp2\", \"io1\")\n\n    if storage_type not in VALID_STORAGE_TYPES:\n        raise ValueError(\n            \"DBInstance StorageType must be one of: %s\" % \", \".join(VALID_STORAGE_TYPES)\n        )\n    return storage_type\n\n\ndef validate_engine(engine):\n    \"\"\"\n    Validate database Engine for DBInstance\n    Property: DBInstance.Engine\n    Property: DBCluster.Engine\n    \"\"\"\n\n    VALID_DB_ENGINES = (\n        \"MySQL\",\n        \"mysql\",\n        \"oracle-se1\",\n        \"oracle-se2\",\n        \"oracle-se\",\n        \"oracle-ee\",\n        \"sqlserver-ee\",\n        \"sqlserver-se\",\n        \"sqlserver-ex\",\n        \"sqlserver-web\",\n        \"postgres\",\n        \"aurora\",\n        \"aurora-mysql\",\n        \"aurora-postgresql\",\n        \"mariadb\",\n    )\n\n    if engine not in VALID_DB_ENGINES:\n        raise ValueError(\n            \"DBInstance Engine must be one of: %s\" % \", \".join(VALID_DB_ENGINES)\n        )\n    return engine\n\n\ndef validate_engine_mode(engine_mode):\n    \"\"\"\n    Validate database EngineMode for DBCluster\n    Property: DBCluster.EngineMode\n    \"\"\"\n\n    VALID_DB_ENGINE_MODES = (\n        \"provisioned\",\n        \"serverless\",\n        \"parallelquery\",\n        \"global\",\n        \"multimaster\",\n    )\n\n    if engine_mode not in VALID_DB_ENGINE_MODES:\n        raise ValueError(\n            \"DBCluster EngineMode must be one of: %s\" % \", \".join(VALID_DB_ENGINE_MODES)\n        )\n    return engine_mode\n\n\ndef validate_license_model(license_model):\n    \"\"\"\n    Validate LicenseModel for DBInstance\n    Property: DBInstance.LicenseModel\n    \"\"\"\n\n    VALID_LICENSE_MODELS = (\n        \"license-included\",\n        \"bring-your-own-license\",\n        \"general-public-license\",\n        \"postgresql-license\",\n    )\n\n    if license_model not in VALID_LICENSE_MODELS:\n        raise ValueError(\n            \"DBInstance LicenseModel must be one of: %s\"\n            % \", \".join(VALID_LICENSE_MODELS)\n        )\n    return license_model\n\n\ndef validate_backup_window(window):\n    \"\"\"\n    Validate PreferredBackupWindow for DBInstance\n    Property: DBInstance.PreferredBackupWindow\n    Property: DBCluster.PreferredBackupWindow\n    \"\"\"\n\n    hour = r\"[01]?[0-9]|2[0-3]\"\n    minute = r\"[0-5][0-9]\"\n    r = (\n        \"(?P<start_hour>%s):(?P<start_minute>%s)-\" \"(?P<end_hour>%s):(?P<end_minute>%s)\"\n    ) % (hour, minute, hour, minute)\n    range_regex = re.compile(r)\n    m = range_regex.match(window)\n    if not m:\n        raise ValueError(\n            \"DBInstance PreferredBackupWindow must be in the \" \"format: hh24:mi-hh24:mi\"\n        )\n    start_ts = (int(m.group(\"start_hour\")) * 60) + int(m.group(\"start_minute\"))\n    end_ts = (int(m.group(\"end_hour\")) * 60) + int(m.group(\"end_minute\"))\n    if abs(end_ts - start_ts) < 30:\n        raise ValueError(\n            \"DBInstance PreferredBackupWindow must be at least \" \"30 minutes long.\"\n        )\n    return window\n\n\ndef validate_maintenance_window(window):\n    \"\"\"\n    Validate PreferredMaintenanceWindow for DBInstance\n    \"\"\"\n\n    days = (\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\")\n    day_re = r\"[A-Z]{1}[a-z]{2}\"\n    hour = r\"[01]?[0-9]|2[0-3]\"\n    minute = r\"[0-5][0-9]\"\n    r = (\n        \"(?P<start_day>%s):(?P<start_hour>%s):(?P<start_minute>%s)-\"\n        \"(?P<end_day>%s):(?P<end_hour>%s):(?P<end_minute>%s)\"\n    ) % (day_re, hour, minute, day_re, hour, minute)\n    range_regex = re.compile(r)\n    m = range_regex.match(window)\n    if not m:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow must be in \"\n            \"the format: ddd:hh24:mi-ddd:hh24:mi\"\n        )\n    if m.group(\"start_day\") not in days or m.group(\"end_day\") not in days:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow day part of \"\n            \"ranges must be one of: %s\" % \", \".join(days)\n        )\n    start_ts = (\n        (days.index(m.group(\"start_day\")) * 24 * 60)\n        + (int(m.group(\"start_hour\")) * 60)\n        + int(m.group(\"start_minute\"))\n    )\n    end_ts = (\n        (days.index(m.group(\"end_day\")) * 24 * 60)\n        + (int(m.group(\"end_hour\")) * 60)\n        + int(m.group(\"end_minute\"))\n    )\n    if abs(end_ts - start_ts) < 30:\n        raise ValueError(\n            \"DBInstance PreferredMaintenanceWindow must be at \" \"least 30 minutes long.\"\n        )\n    return window\n\n\ndef validate_backup_retention_period(days):\n    \"\"\"\n    Validate BackupRetentionPeriod for DBInstance\n    Property: DBInstance.BackupRetentionPeriod\n    Property: DBCluster.BackupRetentionPeriod\n    \"\"\"\n\n    days = positive_integer(days)\n    if int(days) > 35:\n        raise ValueError(\n            \"DBInstance BackupRetentionPeriod cannot be larger \" \"than 35 days.\"\n        )\n    return days\n\n\ndef validate_capacity(capacity):\n    \"\"\"\n    Validate ScalingConfiguration capacity for serverless DBCluster\n    Property: ScalingConfiguration.MaxCapacity\n    Property: ScalingConfiguration.MinCapacity\n    \"\"\"\n\n    VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES = (1, 2, 4, 8, 16, 32, 64, 128, 256)\n    VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES = (2, 4, 8, 16, 32, 64, 192, 384)\n    if (\n        capacity not in VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES\n        and capacity not in VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES\n    ):\n        raise ValueError(\n            \"ScalingConfiguration capacity must be one of: {}\".format(\n                \", \".join(\n                    map(\n                        str,\n                        VALID_MYSQL_SCALING_CONFIGURATION_CAPACITIES\n                        + VALID_POSTGRESL_SCALING_CONFIGURATION_CAPACITIES,\n                    )\n                )\n            )\n        )\n    return capacity\n\n\ndef validate_dbinstance(self) -> None:\n    \"\"\"\n    Class: DBInstance\n    \"\"\"\n    if \"DBSnapshotIdentifier\" not in self.properties:\n        if \"Engine\" not in self.properties:\n            raise ValueError(\n                \"Resource Engine is required in type %s\" % self.resource_type\n            )\n\n    if \"SourceDBInstanceIdentifier\" in self.properties:\n\n        invalid_replica_properties = (\n            \"BackupRetentionPeriod\",\n            \"DBName\",\n            \"MasterUsername\",\n            \"MasterUserPassword\",\n            \"PreferredBackupWindow\",\n            \"MultiAZ\",\n            \"DBSnapshotIdentifier\",\n        )\n\n        invalid_properties = [\n            s for s in self.properties.keys() if s in invalid_replica_properties\n        ]\n\n        if invalid_properties:\n            raise ValueError(\n                (\n                    \"{0} properties can't be provided when \"\n                    \"SourceDBInstanceIdentifier is present \"\n                    \"AWS::RDS::DBInstance.\"\n                ).format(\", \".join(sorted(invalid_properties)))\n            )\n\n    if (\n        (\n            \"DBSnapshotIdentifier\" not in self.properties\n            and \"SourceDBInstanceIdentifier\" not in self.properties\n        )\n        and (\n            \"MasterUsername\" not in self.properties\n            or \"MasterUserPassword\" not in self.properties\n        )\n        and (\"DBClusterIdentifier\" not in self.properties)\n    ):\n        raise ValueError(\n            r\"Either (MasterUsername and MasterUserPassword) or\"\n            r\" DBSnapshotIdentifier are required in type \"\n            r\"AWS::RDS::DBInstance.\"\n        )\n\n    if \"KmsKeyId\" in self.properties and \"StorageEncrypted\" not in self.properties:\n        raise ValueError(\n            \"If KmsKeyId is provided, StorageEncrypted is required \"\n            \"AWS::RDS::DBInstance.\"\n        )\n\n    nonetype = type(None)\n    avail_zone = self.properties.get(\"AvailabilityZone\", None)\n    multi_az = self.properties.get(\"MultiAZ\", None)\n    if not (\n        isinstance(avail_zone, (AWSHelperFn, nonetype))\n        and isinstance(multi_az, (AWSHelperFn, nonetype))\n    ):\n        if avail_zone and multi_az in [True, 1, \"1\", \"true\", \"True\"]:\n            raise ValueError(\n                \"AvailabiltyZone cannot be set on \"\n                \"DBInstance if MultiAZ is set to true.\"\n            )\n\n    storage_type = self.properties.get(\"StorageType\", None)\n    if storage_type and storage_type == \"io1\" and \"Iops\" not in self.properties:\n        raise ValueError(\"Must specify Iops if using StorageType io1\")\n\n    allocated_storage = self.properties.get(\"AllocatedStorage\")\n    iops = self.properties.get(\"Iops\", None)\n    if iops and not isinstance(iops, AWSHelperFn):\n        min_storage_size = 100\n        engine = self.properties.get(\"Engine\")\n        if not isinstance(engine, AWSHelperFn) and engine.startswith(\"sqlserver\"):\n            min_storage_size = 20\n\n        if (\n            not isinstance(allocated_storage, AWSHelperFn)\n            and int(allocated_storage) < min_storage_size\n        ):\n            raise ValueError(\n                f\"AllocatedStorage must be at least {min_storage_size} when \"\n                \"Iops is set.\"\n            )\n        if (\n            not isinstance(allocated_storage, AWSHelperFn)\n            and not isinstance(iops, AWSHelperFn)\n            and float(iops) / float(allocated_storage) > 50.0\n        ):\n            raise ValueError(\n                \"AllocatedStorage must be no less than \" \"1/50th the provisioned Iops\"\n            )\n", "path": "troposphere/validators/rds.py"}]}