1# -------------------------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for license information. 4# -------------------------------------------------------------------------------------------- 5 6# TODO Move this to a package shared by CLI and SDK 7from enum import Enum 8from functools import total_ordering 9from importlib import import_module 10 11from knack.log import get_logger 12 13 14logger = get_logger(__name__) 15 16 17class APIVersionException(Exception): 18 def __init__(self, type_name, api_profile): 19 super(APIVersionException, self).__init__(type_name, api_profile) 20 self.type_name = type_name 21 self.api_profile = api_profile 22 23 def __str__(self): 24 return "Unable to get API version for type '{}' in profile '{}'".format( 25 self.type_name, self.api_profile) 26 27 28# Sentinel value for profile 29PROFILE_TYPE = object() 30 31 32class CustomResourceType: # pylint: disable=too-few-public-methods 33 def __init__(self, import_prefix, client_name): 34 self.import_prefix = import_prefix 35 self.client_name = client_name 36 37 38class ResourceType(Enum): # pylint: disable=too-few-public-methods 39 40 MGMT_APIMANAGEMENT = ('azure.mgmt.apimanagement', 'ApiManagementClient') 41 MGMT_KUSTO = ('azure.mgmt.kusto', 'KustoManagementClient') 42 MGMT_KEYVAULT = ('azure.mgmt.keyvault', 'KeyVaultManagementClient') 43 MGMT_STORAGE = ('azure.mgmt.storage', 'StorageManagementClient') 44 MGMT_COMPUTE = ('azure.mgmt.compute', 'ComputeManagementClient') 45 MGMT_NETWORK = ('azure.mgmt.network', 'NetworkManagementClient') 46 MGMT_NETWORK_DNS = ('azure.mgmt.dns', 'DnsManagementClient') 47 MGMT_AUTHORIZATION = ('azure.mgmt.authorization', 'AuthorizationManagementClient') 48 MGMT_CONTAINERREGISTRY = ('azure.mgmt.containerregistry', 'ContainerRegistryManagementClient') 49 MGMT_RESOURCE_FEATURES = ('azure.mgmt.resource.features', 'FeatureClient') 50 MGMT_RESOURCE_LINKS = ('azure.mgmt.resource.links', 'ManagementLinkClient') 51 MGMT_RESOURCE_LOCKS = ('azure.mgmt.resource.locks', 'ManagementLockClient') 52 MGMT_RESOURCE_POLICY = ('azure.mgmt.resource.policy', 'PolicyClient') 53 MGMT_RESOURCE_RESOURCES = ('azure.mgmt.resource.resources', 'ResourceManagementClient') 54 MGMT_RESOURCE_SUBSCRIPTIONS = ('azure.mgmt.resource.subscriptions', 'SubscriptionClient') 55 MGMT_RESOURCE_DEPLOYMENTSCRIPTS = ('azure.mgmt.resource.deploymentscripts', 'DeploymentScriptsClient') 56 MGMT_RESOURCE_TEMPLATESPECS = ('azure.mgmt.resource.templatespecs', 'TemplateSpecsClient') 57 MGMT_MONITOR = ('azure.mgmt.monitor', 'MonitorManagementClient') 58 DATA_KEYVAULT = ('azure.keyvault', 'KeyVaultClient') 59 DATA_KEYVAULT_KEYS = ('azure.keyvault.keys', 'KeyClient') 60 DATA_PRIVATE_KEYVAULT = ('azure.cli.command_modules.keyvault.vendored_sdks.azure_keyvault_t1', 'KeyVaultClient') 61 DATA_KEYVAULT_ADMINISTRATION_BACKUP = ('azure.keyvault.administration', 'KeyVaultBackupClient') 62 DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL = ('azure.keyvault.administration', 'KeyVaultAccessControlClient') 63 MGMT_EVENTHUB = ('azure.mgmt.eventhub', 'EventHubManagementClient') 64 MGMT_APPSERVICE = ('azure.mgmt.web', 'WebSiteManagementClient') 65 MGMT_IOTCENTRAL = ('azure.mgmt.iotcentral', 'IotCentralClient') 66 MGMT_IOTHUB = ('azure.mgmt.iothub', 'IotHubClient') 67 MGMT_ARO = ('azure.mgmt.redhatopenshift', 'AzureRedHatOpenShift4Client') 68 MGMT_DATABOXEDGE = ('azure.mgmt.databoxedge', 'DataBoxEdgeManagementClient') 69 MGMT_CUSTOMLOCATION = ('azure.mgmt.extendedlocation', 'CustomLocations') 70 MGMT_CONTAINERSERVICE = ('azure.mgmt.containerservice', 'ContainerServiceClient') 71 # the "None" below will stay till a command module fills in the type so "get_mgmt_service_client" 72 # can be provided with "ResourceType.XXX" to initialize the client object. This usually happens 73 # when related commands start to support Multi-API 74 75 DATA_COSMOS_TABLE = ('azure.multiapi.cosmosdb', None) 76 MGMT_ADVISOR = ('azure.mgmt.advisor', None) 77 MGMT_MEDIA = ('azure.mgmt.media', None) 78 MGMT_BACKUP = ('azure.mgmt.recoveryservicesbackup', None) 79 MGMT_BATCH = ('azure.mgmt.batch', None) 80 MGMT_BATCHAI = ('azure.mgmt.batchai', None) 81 MGMT_BILLING = ('azure.mgmt.billing', None) 82 MGMT_BOTSERVICE = ('azure.mgmt.botservice', None) 83 MGMT_CDN = ('azure.mgmt.cdn', None) 84 MGMT_COGNITIVESERVICES = ('azure.mgmt.cognitiveservices', None) 85 MGMT_CONSUMPTION = ('azure.mgmt.consumption', None) 86 MGMT_CONTAINERINSTANCE = ('azure.mgmt.containerinstance', None) 87 MGMT_COSMOSDB = ('azure.mgmt.cosmosdb', None) 88 MGMT_DEPLOYMENTMANAGER = ('azure.mgmt.deploymentmanager', None) 89 MGMT_DATALAKE_ANALYTICS = ('azure.mgmt.datalake.analytics', None) 90 MGMT_DATALAKE_STORE = ('azure.mgmt.datalake.store', None) 91 MGMT_DATAMIGRATION = ('azure.mgmt.datamigration', None) 92 MGMT_EVENTGRID = ('azure.mgmt.eventgrid', None) 93 MGMT_DEVTESTLABS = ('azure.mgmt.devtestlabs', None) 94 MGMT_MAPS = ('azure.mgmt.maps', None) 95 MGMT_POLICYINSIGHTS = ('azure.mgmt.policyinsights', None) 96 MGMT_RDBMS = ('azure.mgmt.rdbms', None) 97 MGMT_REDIS = ('azure.mgmt.redis', None) 98 MGMT_RELAY = ('azure.mgmt.relay', None) 99 MGMT_RESERVATIONS = ('azure.mgmt.reservations', None) 100 MGMT_SEARCH = ('azure.mgmt.search', None) 101 MGMT_SERVICEBUS = ('azure.mgmt.servicebus', None) 102 MGMT_SERVICEFABRIC = ('azure.mgmt.servicefabric', None) 103 MGMT_SIGNALR = ('azure.mgmt.signalr', None) 104 MGMT_SQL = ('azure.mgmt.sql', None) 105 MGMT_SQLVM = ('azure.mgmt.sqlvirtualmachine', None) 106 MGMT_MANAGEDSERVICES = ('azure.mgmt.managedservices', None) 107 MGMT_NETAPPFILES = ('azure.mgmt.netappfiles', None) 108 DATA_STORAGE = ('azure.multiapi.storage', None) 109 DATA_STORAGE_BLOB = ('azure.multiapi.storagev2.blob', None) 110 DATA_STORAGE_FILEDATALAKE = ('azure.multiapi.storagev2.filedatalake', None) 111 DATA_STORAGE_FILESHARE = ('azure.multiapi.storagev2.fileshare', None) 112 DATA_STORAGE_QUEUE = ('azure.multiapi.storagev2.queue', None) 113 114 def __init__(self, import_prefix, client_name): 115 """Constructor. 116 117 :param import_prefix: Path to the (unversioned) module. 118 :type import_prefix: str. 119 :param client_name: Name the client for this resource type. 120 :type client_name: str. 121 """ 122 self.import_prefix = import_prefix 123 self.client_name = client_name 124 125 126class SDKProfile: # pylint: disable=too-few-public-methods 127 128 def __init__(self, default_api_version, profile=None): 129 """Constructor. 130 131 :param str default_api_version: Default API version if not overridden by a profile. Nullable. 132 :param profile: A dict operation group name to API version. 133 :type profile: dict[str, str] 134 """ 135 self.profile = profile if profile is not None else {} 136 self.profile[None] = default_api_version 137 138 @property 139 def default_api_version(self): 140 return self.profile[None] 141 142 143AZURE_API_PROFILES = { 144 'latest': { 145 ResourceType.MGMT_STORAGE: '2021-06-01', 146 ResourceType.MGMT_NETWORK: '2021-02-01', 147 ResourceType.MGMT_COMPUTE: SDKProfile('2021-07-01', { 148 'resource_skus': '2019-04-01', 149 'disks': '2020-12-01', 150 'disk_encryption_sets': '2020-12-01', 151 'disk_accesses': '2020-05-01', 152 'snapshots': '2020-12-01', 153 'galleries': '2021-07-01', 154 'gallery_images': '2020-09-30', 155 'gallery_image_versions': '2021-07-01', 156 'shared_galleries': '2020-09-30', 157 'virtual_machine_scale_sets': '2021-04-01', 158 }), 159 ResourceType.MGMT_RESOURCE_FEATURES: '2021-07-01', 160 ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', 161 ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', 162 ResourceType.MGMT_RESOURCE_POLICY: '2020-09-01', 163 ResourceType.MGMT_RESOURCE_RESOURCES: '2021-04-01', 164 ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2019-11-01', 165 ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2020-10-01', 166 ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2021-05-01', 167 ResourceType.MGMT_NETWORK_DNS: '2018-05-01', 168 ResourceType.MGMT_KEYVAULT: '2021-04-01-preview', 169 ResourceType.MGMT_AUTHORIZATION: SDKProfile('2020-04-01-preview', { 170 'classic_administrators': '2015-06-01', 171 'role_definitions': '2018-01-01-preview', 172 'provider_operations_metadata': '2018-01-01-preview' 173 }), 174 ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2021-06-01-preview', { 175 'agent_pools': '2019-06-01-preview', 176 'tasks': '2019-06-01-preview', 177 'task_runs': '2019-06-01-preview', 178 'runs': '2019-06-01-preview', 179 }), 180 # The order does make things different. 181 # Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT 182 ResourceType.DATA_KEYVAULT_KEYS: None, 183 ResourceType.DATA_KEYVAULT: '7.0', 184 ResourceType.DATA_PRIVATE_KEYVAULT: '7.2', 185 ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP: '7.2-preview', 186 ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL: '7.2-preview', 187 ResourceType.DATA_STORAGE: '2018-11-09', 188 ResourceType.DATA_STORAGE_BLOB: '2020-04-08', 189 ResourceType.DATA_STORAGE_FILEDATALAKE: '2020-02-10', 190 ResourceType.DATA_STORAGE_FILESHARE: '2019-07-07', 191 ResourceType.DATA_STORAGE_QUEUE: '2018-03-28', 192 ResourceType.DATA_COSMOS_TABLE: '2017-04-17', 193 ResourceType.MGMT_EVENTHUB: '2021-06-01-preview', 194 ResourceType.MGMT_MONITOR: SDKProfile('2019-06-01', { 195 'activity_log_alerts': '2017-04-01', 196 'activity_logs': '2015-04-01', 197 'alert_rule_incidents': '2016-03-01', 198 'alert_rules': '2016-03-01', 199 'autoscale_settings': '2015-04-01', 200 'baseline': '2018-09-01', 201 'baselines': '2019-03-01', 202 'diagnostic_settings': '2017-05-01-preview', 203 'diagnostic_settings_category': '2017-05-01-preview', 204 'event_categories': '2015-04-01', 205 'guest_diagnostics_settings': '2018-06-01-preview', 206 'guest_diagnostics_settings_association': '2018-06-01-preview', 207 'log_profiles': '2016-03-01', 208 'metric_alerts': '2018-03-01', 209 'metric_alerts_status': '2018-03-01', 210 'metric_baseline': '2018-09-01', 211 'metric_definitions': '2018-01-01', 212 'metric_namespaces': '2017-12-01-preview', 213 'metrics': '2018-01-01', 214 'operations': '2015-04-01', 215 'scheduled_query_rules': '2018-04-16', 216 'service_diagnostic_settings': '2016-09-01', 217 'tenant_activity_logs': '2015-04-01', 218 'vm_insights': '2018-11-27-preview', 219 'private_link_resources': '2019-10-17-preview', 220 'private_link_scoped_resources': '2019-10-17-preview', 221 'private_link_scope_operation_status': '2019-10-17-preview', 222 'private_link_scopes': '2019-10-17-preview', 223 'private_endpoint_connections': '2019-10-17-preview', 224 'subscription_diagnostic_settings': '2017-05-01-preview' 225 }), 226 ResourceType.MGMT_APPSERVICE: '2020-09-01', 227 ResourceType.MGMT_IOTHUB: '2021-07-01', 228 ResourceType.MGMT_IOTCENTRAL: '2018-09-01', 229 ResourceType.MGMT_ARO: '2020-04-30', 230 ResourceType.MGMT_DATABOXEDGE: '2021-02-01-preview', 231 ResourceType.MGMT_CUSTOMLOCATION: '2021-03-15-preview', 232 ResourceType.MGMT_CONTAINERSERVICE: SDKProfile('2021-07-01', { 233 'container_services': '2017-07-01', 234 'open_shift_managed_clusters': '2019-09-30-preview' 235 }) 236 }, 237 '2020-09-01-hybrid': { 238 ResourceType.MGMT_STORAGE: '2019-06-01', 239 ResourceType.MGMT_NETWORK: '2018-11-01', 240 ResourceType.MGMT_COMPUTE: SDKProfile('2020-06-01', { 241 'resource_skus': '2019-04-01', 242 'disks': '2019-07-01', 243 'disk_encryption_sets': '2019-07-01', 244 'disk_accesses': '2020-05-01', 245 'snapshots': '2019-07-01', 246 'galleries': '2019-12-01', 247 'gallery_images': '2019-12-01', 248 'gallery_image_versions': '2019-12-01', 249 'virtual_machine_scale_sets': '2020-06-01' 250 }), 251 ResourceType.MGMT_KEYVAULT: '2016-10-01', 252 ResourceType.MGMT_RESOURCE_FEATURES: '2021-07-01', 253 ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', 254 ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', 255 ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01', 256 ResourceType.MGMT_RESOURCE_RESOURCES: '2019-10-01', 257 ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', 258 ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01', 259 ResourceType.MGMT_NETWORK_DNS: '2016-04-01', 260 ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { 261 'classic_administrators': '2015-06-01', 262 'policy_assignments': '2016-12-01', 263 'policy_definitions': '2016-12-01' 264 }), 265 # The order does make things different. 266 # Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT 267 ResourceType.DATA_KEYVAULT_KEYS: None, 268 ResourceType.DATA_KEYVAULT: '2016-10-01', 269 ResourceType.DATA_STORAGE: '2018-11-09', 270 ResourceType.DATA_STORAGE_BLOB: '2019-07-07', 271 ResourceType.DATA_STORAGE_FILEDATALAKE: '2019-07-07', 272 ResourceType.DATA_STORAGE_FILESHARE: '2019-07-07', 273 ResourceType.DATA_STORAGE_QUEUE: '2019-07-07', 274 ResourceType.DATA_COSMOS_TABLE: '2017-04-17', 275 ResourceType.MGMT_APPSERVICE: '2018-02-01', 276 ResourceType.MGMT_EVENTHUB: '2021-06-01-preview', 277 ResourceType.MGMT_IOTHUB: '2019-07-01-preview', 278 ResourceType.MGMT_DATABOXEDGE: '2019-08-01', 279 ResourceType.MGMT_CONTAINERREGISTRY: '2019-05-01', 280 ResourceType.MGMT_CONTAINERSERVICE: SDKProfile('2020-11-01', { 281 'container_services': '2017-07-01', 282 'open_shift_managed_clusters': '2019-09-30-preview' 283 }) 284 }, 285 '2019-03-01-hybrid': { 286 ResourceType.MGMT_STORAGE: '2017-10-01', 287 ResourceType.MGMT_NETWORK: '2017-10-01', 288 ResourceType.MGMT_COMPUTE: SDKProfile('2017-12-01', { 289 'resource_skus': '2017-09-01', 290 'disks': '2017-03-30', 291 'snapshots': '2017-03-30' 292 }), 293 ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', 294 ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', 295 ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01', 296 ResourceType.MGMT_RESOURCE_RESOURCES: '2018-05-01', 297 ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', 298 ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01', 299 ResourceType.MGMT_NETWORK_DNS: '2016-04-01', 300 ResourceType.MGMT_KEYVAULT: '2016-10-01', 301 ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { 302 'classic_administrators': '2015-06-01', 303 'policy_assignments': '2016-12-01', 304 'policy_definitions': '2016-12-01' 305 }), 306 # The order does make things different. 307 # Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT 308 ResourceType.DATA_KEYVAULT_KEYS: None, 309 ResourceType.DATA_KEYVAULT: '2016-10-01', 310 ResourceType.DATA_STORAGE: '2017-11-09', 311 ResourceType.DATA_STORAGE_BLOB: '2017-11-09', 312 ResourceType.DATA_STORAGE_FILEDATALAKE: '2017-11-09', 313 ResourceType.DATA_STORAGE_FILESHARE: '2017-11-09', 314 ResourceType.DATA_STORAGE_QUEUE: '2017-11-09', 315 ResourceType.DATA_COSMOS_TABLE: '2017-04-17', 316 # Full MultiAPI support is not done in AppService, the line below is merely 317 # to have commands show up in the hybrid profile which happens to have the latest 318 # API versions 319 ResourceType.MGMT_APPSERVICE: '2018-02-01', 320 ResourceType.MGMT_EVENTHUB: '2021-06-01-preview', 321 ResourceType.MGMT_IOTHUB: '2019-03-22', 322 ResourceType.MGMT_DATABOXEDGE: '2019-08-01' 323 }, 324 '2018-03-01-hybrid': { 325 ResourceType.MGMT_STORAGE: '2016-01-01', 326 ResourceType.MGMT_NETWORK: '2017-10-01', 327 ResourceType.MGMT_COMPUTE: SDKProfile('2017-03-30'), 328 ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', 329 ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', 330 ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01', 331 ResourceType.MGMT_RESOURCE_RESOURCES: '2018-02-01', 332 ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', 333 ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01', 334 ResourceType.MGMT_NETWORK_DNS: '2016-04-01', 335 ResourceType.MGMT_KEYVAULT: '2016-10-01', 336 ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { 337 'classic_administrators': '2015-06-01' 338 }), 339 # The order does make things different. 340 # Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT 341 ResourceType.DATA_KEYVAULT_KEYS: None, 342 ResourceType.DATA_KEYVAULT: '2016-10-01', 343 ResourceType.DATA_STORAGE: '2017-04-17', 344 ResourceType.DATA_STORAGE_BLOB: '2017-04-17', 345 ResourceType.DATA_STORAGE_FILEDATALAKE: '2017-04-17', 346 ResourceType.DATA_STORAGE_FILESHARE: '2017-04-17', 347 ResourceType.DATA_STORAGE_QUEUE: '2017-04-17', 348 ResourceType.DATA_COSMOS_TABLE: '2017-04-17' 349 }, 350 '2017-03-09-profile': { 351 ResourceType.MGMT_STORAGE: '2016-01-01', 352 ResourceType.MGMT_NETWORK: '2015-06-15', 353 ResourceType.MGMT_COMPUTE: SDKProfile('2016-03-30'), 354 ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', 355 ResourceType.MGMT_RESOURCE_LOCKS: '2015-01-01', 356 ResourceType.MGMT_RESOURCE_POLICY: '2015-10-01-preview', 357 ResourceType.MGMT_RESOURCE_RESOURCES: '2016-02-01', 358 ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', 359 ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2015-01-01', 360 ResourceType.MGMT_NETWORK_DNS: '2016-04-01', 361 ResourceType.MGMT_KEYVAULT: '2016-10-01', 362 ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { 363 'classic_administrators': '2015-06-01' 364 }), 365 # The order does make things different. 366 # Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT 367 ResourceType.DATA_KEYVAULT_KEYS: None, 368 ResourceType.DATA_KEYVAULT: '2016-10-01', 369 ResourceType.DATA_STORAGE: '2015-04-05', 370 ResourceType.DATA_STORAGE_BLOB: '2015-04-05', 371 ResourceType.DATA_STORAGE_FILEDATALAKE: '2015-04-05', 372 ResourceType.DATA_STORAGE_FILESHARE: '2015-04-05', 373 ResourceType.DATA_STORAGE_QUEUE: '2015-04-05' 374 } 375} 376 377 378# We should avoid using ad hoc API versions, 379# use the version in a profile as much as possible. 380AD_HOC_API_VERSIONS = { 381 ResourceType.MGMT_NETWORK: { 382 'vm_default_target_network': '2018-01-01', 383 'nw_connection_monitor': '2019-06-01', 384 'container_network': '2018-08-01', 385 'appservice_network': '2020-04-01', 386 'appservice_ensure_subnet': '2019-02-01' 387 } 388} 389 390 391class _ApiVersions: # pylint: disable=too-few-public-methods 392 def __init__(self, client_type, sdk_profile, post_process): 393 self._client_type = client_type 394 self._sdk_profile = sdk_profile 395 self._post_process = post_process 396 self._operations_groups_value = None 397 self._resolved = False 398 399 def _resolve(self): 400 if self._resolved: 401 return 402 403 self._operations_groups_value = {} 404 for operation_group_name, operation_type in self._client_type.__dict__.items(): 405 if not isinstance(operation_type, property): 406 continue 407 408 value_to_save = self._sdk_profile.profile.get( 409 operation_group_name, 410 self._sdk_profile.default_api_version 411 ) 412 self._operations_groups_value[operation_group_name] = self._post_process(value_to_save) 413 self._resolved = True 414 415 def __getattr__(self, item): 416 try: 417 self._resolve() 418 return self._operations_groups_value[item] 419 except KeyError: 420 raise AttributeError('Attribute {} does not exist.'.format(item)) 421 422 423def _get_api_version_tuple(resource_type, sdk_profile, post_process=lambda x: x): 424 """Return a _ApiVersion instance where key are operation group and value are api version.""" 425 return _ApiVersions(client_type=get_client_class(resource_type), 426 sdk_profile=sdk_profile, 427 post_process=post_process) 428 429 430def get_api_version(api_profile, resource_type, as_sdk_profile=False): 431 """Get the API version of a resource type given an API profile. 432 433 :param api_profile: The name of the API profile. 434 :type api_profile: str. 435 :param resource_type: The resource type. 436 :type resource_type: ResourceType. 437 :returns: str -- the API version. 438 :raises: APIVersionException 439 """ 440 try: 441 api_version = AZURE_API_PROFILES[api_profile][resource_type] 442 if as_sdk_profile: 443 return api_version # Could be SDKProfile or string 444 if isinstance(api_version, SDKProfile): 445 api_version = _get_api_version_tuple(resource_type, api_version) 446 return api_version 447 except KeyError: 448 raise APIVersionException(resource_type, api_profile) 449 450 451@total_ordering 452class _SemVerAPIFormat: 453 """Basic semver x.y.z API format. 454 Supports x, or x.y, or x.y.z 455 """ 456 457 def __init__(self, api_version_str): 458 try: 459 parts = api_version_str.split('.') 460 parts += [0, 0] # At worst never read, at best minor/patch 461 self.major = int(parts[0]) 462 self.minor = int(parts[1]) 463 self.patch = int(parts[2]) 464 except (ValueError, TypeError): 465 raise ValueError('The API version {} is not in a ' 466 'semver format'.format(api_version_str)) 467 468 def __eq__(self, other): 469 return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch) 470 471 def __lt__(self, other): 472 return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch) 473 474 475@total_ordering # pylint: disable=too-few-public-methods 476class _DateAPIFormat: 477 """ Class to support comparisons for API versions in 478 YYYY-MM-DD, YYYY-MM-DD-preview, YYYY-MM-DD-profile, YYYY-MM-DD-profile-preview 479 or any string that starts with YYYY-MM-DD format. A special case is made for 'latest'. 480 """ 481 482 def __init__(self, api_version_str): 483 try: 484 self.latest = self.preview = False 485 self.yyyy = self.mm = self.dd = None 486 if api_version_str == 'latest': 487 self.latest = True 488 else: 489 if 'preview' in api_version_str: 490 self.preview = True 491 parts = api_version_str.split('-') 492 self.yyyy = int(parts[0]) 493 self.mm = int(parts[1]) 494 self.dd = int(parts[2]) 495 except (ValueError, TypeError): 496 raise ValueError('The API version {} is not in a ' 497 'supported format'.format(api_version_str)) 498 499 def __eq__(self, other): 500 return self.latest == other.latest and self.yyyy == other.yyyy and self.mm == other.mm and \ 501 self.dd == other.dd and self.preview == other.preview 502 503 def __lt__(self, other): # pylint: disable=too-many-return-statements 504 if self.latest or other.latest: 505 if not self.latest and other.latest: 506 return True 507 if self.latest and not other.latest: 508 return False 509 return False 510 if self.yyyy < other.yyyy: 511 return True 512 if self.yyyy == other.yyyy: 513 if self.mm < other.mm: 514 return True 515 if self.mm == other.mm: 516 if self.dd < other.dd: 517 return True 518 if self.dd == other.dd: 519 if self.preview and not other.preview: 520 return True 521 return False 522 523 524def _parse_api_version(api_version): 525 """Will try to parse it as a date, and if not working 526 as semver, and if still not working raise. 527 """ 528 try: 529 return _DateAPIFormat(api_version) 530 except ValueError: 531 return _SemVerAPIFormat(api_version) 532 533 534def _cross_api_format_less_than(api_version, other): 535 """LT strategy that supports if types are different. 536 537 For now, let's assume that any Semver is higher than any DateAPI 538 This fits KeyVault, if later we have a counter-example we'll update 539 """ 540 api_version = _parse_api_version(api_version) 541 other = _parse_api_version(other) 542 543 if type(api_version) is type(other): 544 return api_version < other 545 return isinstance(api_version, _DateAPIFormat) and isinstance(other, _SemVerAPIFormat) 546 547 548def _validate_api_version(api_version_str, min_api=None, max_api=None): 549 """Validate if api_version is inside the interval min_api/max_api. 550 """ 551 if min_api and _cross_api_format_less_than(api_version_str, min_api): 552 return False 553 if max_api and _cross_api_format_less_than(max_api, api_version_str): 554 return False 555 return True 556 557 558def supported_api_version(api_profile, resource_type, min_api=None, max_api=None, operation_group=None): 559 """ 560 Returns True if current API version for the resource type satisfies min/max range. 561 To compare profile versions, set resource type to None. 562 Can return a tuple<operation_group, bool> if the resource_type supports SDKProfile. 563 note: Currently supports YYYY-MM-DD, YYYY-MM-DD-preview, YYYY-MM-DD-profile 564 or YYYY-MM-DD-profile-preview formatted strings. 565 """ 566 if not isinstance(resource_type, (ResourceType, CustomResourceType)) and resource_type != PROFILE_TYPE: 567 raise ValueError("'resource_type' is required.") 568 if min_api is None and max_api is None: 569 raise ValueError('At least a min or max version must be specified') 570 api_version_obj = get_api_version(api_profile, resource_type, as_sdk_profile=True) \ 571 if isinstance(resource_type, (ResourceType, CustomResourceType)) else api_profile 572 if isinstance(api_version_obj, SDKProfile): 573 api_version_obj = api_version_obj.profile.get(operation_group or '', api_version_obj.default_api_version) 574 return _validate_api_version(api_version_obj, min_api, max_api) 575 576 577def supported_resource_type(api_profile, resource_type): 578 if api_profile == 'latest' or resource_type is None: 579 return True 580 try: 581 return bool(AZURE_API_PROFILES[api_profile][resource_type]) 582 except KeyError: 583 return False 584 585 586def _get_attr(sdk_path, mod_attr_path, checked=True): 587 try: 588 attr_mod, attr_path = mod_attr_path.split('#') \ 589 if '#' in mod_attr_path else (mod_attr_path, '') 590 full_mod_path = '{}.{}'.format(sdk_path, attr_mod) if attr_mod else sdk_path 591 op = import_module(full_mod_path) 592 if attr_path: 593 # Only load attributes if needed 594 for part in attr_path.split('.'): 595 op = getattr(op, part) 596 return op 597 except (ImportError, AttributeError) as ex: 598 import traceback 599 logger.debug(traceback.format_exc()) 600 if checked: 601 return None 602 raise ex 603 604 605def get_client_class(resource_type): 606 return _get_attr(resource_type.import_prefix, '#' + resource_type.client_name) 607 608 609def get_versioned_sdk_path(api_profile, resource_type, operation_group=None): 610 """ Patch the unversioned sdk path to include the appropriate API version for the 611 resource type in question. 612 e.g. Converts azure.mgmt.storage.operations.storage_accounts_operations to 613 azure.mgmt.storage.v2016_12_01.operations.storage_accounts_operations 614 azure.keyvault.v7_0.models.KeyVault 615 """ 616 api_version = get_api_version(api_profile, resource_type) 617 if api_version is None: 618 return resource_type.import_prefix 619 if isinstance(api_version, _ApiVersions): 620 if operation_group is None: 621 raise ValueError("operation_group is required for resource type '{}'".format(resource_type)) 622 api_version = getattr(api_version, operation_group) 623 return '{}.v{}'.format(resource_type.import_prefix, api_version.replace('-', '_').replace('.', '_')) 624 625 626def get_versioned_sdk(api_profile, resource_type, *attr_args, **kwargs): 627 checked = kwargs.get('checked', True) 628 sub_mod_prefix = kwargs.get('mod', None) 629 operation_group = kwargs.get('operation_group', None) 630 sdk_path = get_versioned_sdk_path(api_profile, resource_type, operation_group) 631 if not attr_args: 632 # No attributes to load. Return the versioned sdk 633 return import_module(sdk_path) 634 results = [] 635 for mod_attr_path in attr_args: 636 if sub_mod_prefix and '#' not in mod_attr_path: 637 mod_attr_path = '{}#{}'.format(sub_mod_prefix, mod_attr_path) 638 loaded_obj = _get_attr(sdk_path, mod_attr_path, checked) 639 results.append(loaded_obj) 640 return results[0] if len(results) == 1 else results 641