Skip to content

Commit f347b33

Browse files
authored
[Storage] az storage account create/update, az storage account network-rule add/remove: Support IPv6 preview (#26826)
1 parent d135371 commit f347b33

5 files changed

Lines changed: 920 additions & 29 deletions

File tree

src/azure-cli/azure/cli/command_modules/storage/_params.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
446446
c.argument('enable_blob_geo_priority_replication', arg_type=get_three_state_flag(),
447447
options_list=['--enable-blob-geo-priority-replication', '--blob-geo-sla'],
448448
help='Indicates whether Blob Geo Priority Replication is enabled for the storage account.')
449+
c.argument('publish_ipv6_endpoint', arg_type=get_three_state_flag(),
450+
arg_group='IPv6 Endpoint', is_preview=True,
451+
help='A boolean flag which indicates whether IPv6 storage endpoints are to be published.')
449452

450453
with self.argument_context('storage account private-endpoint-connection',
451454
resource_type=ResourceType.MGMT_STORAGE) as c:
@@ -547,6 +550,9 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
547550
c.argument('enable_blob_geo_priority_replication', arg_type=get_three_state_flag(),
548551
options_list=['--enable-blob-geo-priority-replication', '--blob-geo-sla'],
549552
help='Indicates whether Blob Geo Priority Replication is enabled for the storage account.')
553+
c.argument('publish_ipv6_endpoint', arg_type=get_three_state_flag(),
554+
arg_group='IPv6 Endpoint', is_preview=True,
555+
help='A boolean flag which indicates whether IPv6 storage endpoints are to be published.')
550556

551557
for scope in ['storage account create', 'storage account update']:
552558
with self.argument_context(scope, arg_group='Customer managed key',
@@ -659,10 +665,12 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
659665
c.argument('account_name', acct_name_type, id_part=None)
660666

661667
with self.argument_context('storage account network-rule', resource_type=ResourceType.MGMT_STORAGE) as c:
662-
from ._validators import validate_ip_address
668+
from ._validators import validate_ip_address, validate_ipv6_address
663669
c.argument('account_name', acct_name_type, id_part=None)
664670
c.argument('ip_address', nargs='*', help='IPv4 address or CIDR range. Can supply a list: --ip-address ip1 '
665671
'[ip2]...', validator=validate_ip_address)
672+
c.argument('ipv6_address', nargs='*', help='IPv6 address or CIDR range. Can supply a list: --ipv6-address ip1 '
673+
'[ip2]...', validator=validate_ipv6_address, is_preview=True)
666674
c.argument('subnet', help='Name or ID of subnet. If name is supplied, `--vnet-name` must be supplied.')
667675
c.argument('vnet_name', help='Name of a virtual network.', validator=validate_subnet)
668676
c.argument('action', action_type)

src/azure-cli/azure/cli/command_modules/storage/_validators.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,22 +2122,41 @@ def validate_fs_file_set_expiry(namespace):
21222122
pass
21232123

21242124

2125-
def validate_ip_address(namespace):
2126-
# if there are overlapping ip ranges, throw an exception
2127-
ip_address = namespace.ip_address
2128-
2125+
def _find_ip_address_overlap(ip_address, ipv6=False):
21292126
if not ip_address:
21302127
return
21312128

2129+
from azure.cli.core.azclierror import InvalidArgumentValueError
21322130
ip_address_networks = [ip_network(ip) for ip in ip_address]
2131+
if ipv6:
2132+
ipv4_address_in_ipv6 = [ip for ip in ip_address_networks if ip.version != 6]
2133+
if ipv4_address_in_ipv6:
2134+
raise InvalidArgumentValueError(f"ipv4 addresses {ipv4_address_in_ipv6} found in --ipv6-address")
2135+
else:
2136+
ipv6_address_in_ipv4 = [ip for ip in ip_address_networks if ip.version == 6]
2137+
if ipv6_address_in_ipv4:
2138+
raise InvalidArgumentValueError(f"ipv6 addresses {ipv6_address_in_ipv4} found in --ip-address")
2139+
2140+
error_str = "ipv6 addresses {} and {} provided are overlapping: --ipv6-address ip1 [ip2]..." if ipv6 else \
2141+
"ip addresses {} and {} provided are overlapping: --ip-address ip1 [ip2]..."
21332142
for idx, ip_address_network in enumerate(ip_address_networks):
21342143
for idx2, ip_address_network2 in enumerate(ip_address_networks):
21352144
if idx == idx2:
21362145
continue
21372146
if ip_address_network.overlaps(ip_address_network2):
2138-
from azure.cli.core.azclierror import InvalidArgumentValueError
2139-
raise InvalidArgumentValueError(f"ip addresses {ip_address_network} and {ip_address_network2} "
2140-
f"provided are overlapping: --ip_address ip1 [ip2]...")
2147+
raise InvalidArgumentValueError(error_str.format(ip_address_network, ip_address_network2))
2148+
2149+
2150+
def validate_ip_address(namespace):
2151+
# if there are overlapping ip ranges, throw an exception
2152+
ip_address = namespace.ip_address
2153+
_find_ip_address_overlap(ip_address=ip_address, ipv6=False)
2154+
2155+
2156+
def validate_ipv6_address(namespace):
2157+
# if there are overlapping ip ranges, throw an exception
2158+
ipv6_address = namespace.ipv6_address
2159+
_find_ip_address_overlap(ip_address=ipv6_address, ipv6=True)
21412160

21422161

21432162
# pylint: disable=too-few-public-methods

src/azure-cli/azure/cli/command_modules/storage/operations/account.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def create_storage_account(cmd, resource_group_name, account_name, sku=None, loc
7979
immutability_period_since_creation_in_days=None, immutability_policy_state=None,
8080
allow_protected_append_writes=None, public_network_access=None, dns_endpoint_type=None,
8181
enable_smb_oauth=None, zones=None, zone_placement_policy=None,
82-
enable_blob_geo_priority_replication=None):
82+
enable_blob_geo_priority_replication=None, publish_ipv6_endpoint=None):
8383
StorageAccountCreateParameters, Kind, Sku, CustomDomain, AccessTier, Identity, Encryption, NetworkRuleSet = \
8484
cmd.get_models('StorageAccountCreateParameters', 'Kind', 'Sku', 'CustomDomain', 'AccessTier', 'Identity',
8585
'Encryption', 'NetworkRuleSet')
@@ -325,6 +325,12 @@ def create_storage_account(cmd, resource_group_name, account_name, sku=None, loc
325325
GeoPriorityReplicationStatus = cmd.get_models('GeoPriorityReplicationStatus')
326326
params.geo_priority_replication_status = GeoPriorityReplicationStatus(is_blob_enabled=enable_blob_geo_priority_replication)
327327

328+
if publish_ipv6_endpoint is not None:
329+
DualStackEndpointPreference = cmd.get_models('DualStackEndpointPreference')
330+
params.dual_stack_endpoint_preference = DualStackEndpointPreference(
331+
publish_ipv6_endpoint=publish_ipv6_endpoint
332+
)
333+
328334
return scf.storage_accounts.begin_create(resource_group_name, account_name, params)
329335

330336

@@ -420,7 +426,7 @@ def update_storage_account(cmd, instance, sku=None, tags=None, custom_domain=Non
420426
immutability_period_since_creation_in_days=None, immutability_policy_state=None,
421427
allow_protected_append_writes=None, public_network_access=None, upgrade_to_storagev2=None,
422428
yes=None, enable_smb_oauth=None, zones=None, zone_placement_policy=None,
423-
enable_blob_geo_priority_replication=None):
429+
enable_blob_geo_priority_replication=None, publish_ipv6_endpoint=None):
424430
StorageAccountUpdateParameters, Sku, CustomDomain, AccessTier, Identity, Encryption, NetworkRuleSet, Kind = \
425431
cmd.get_models('StorageAccountUpdateParameters', 'Sku', 'CustomDomain', 'AccessTier', 'Identity', 'Encryption',
426432
'NetworkRuleSet', 'Kind')
@@ -734,6 +740,12 @@ def update_storage_account(cmd, instance, sku=None, tags=None, custom_domain=Non
734740
GeoPriorityReplicationStatus = cmd.get_models('GeoPriorityReplicationStatus')
735741
params.geo_priority_replication_status = GeoPriorityReplicationStatus(is_blob_enabled=enable_blob_geo_priority_replication)
736742

743+
if publish_ipv6_endpoint is not None:
744+
DualStackEndpointPreference = cmd.get_models('DualStackEndpointPreference')
745+
params.dual_stack_endpoint_preference = DualStackEndpointPreference(
746+
publish_ipv6_endpoint=publish_ipv6_endpoint
747+
)
748+
737749
return params
738750

739751

@@ -746,11 +758,12 @@ def list_network_rules(client, resource_group_name, account_name):
746758

747759

748760
def add_network_rule(cmd, client, resource_group_name, account_name, action='Allow', subnet=None,
749-
vnet_name=None, ip_address=None, tenant_id=None, resource_id=None): # pylint: disable=unused-argument
761+
vnet_name=None, ip_address=None, ipv6_address=None, tenant_id=None, resource_id=None): # pylint: disable=unused-argument
750762
sa = client.get_properties(resource_group_name, account_name)
751763
rules = sa.network_rule_set
752-
if not subnet and not ip_address:
764+
if not subnet and not ip_address and not ipv6_address:
753765
logger.warning('No subnet or ip address supplied.')
766+
754767
if subnet:
755768
from azure.mgmt.core.tools import is_valid_resource_id
756769
if not is_valid_resource_id(subnet):
@@ -761,22 +774,13 @@ def add_network_rule(cmd, client, resource_group_name, account_name, action='All
761774
rules.virtual_network_rules = [r for r in rules.virtual_network_rules
762775
if r.virtual_network_resource_id.lower() != subnet.lower()]
763776
rules.virtual_network_rules.append(VirtualNetworkRule(virtual_network_resource_id=subnet, action=action))
777+
764778
if ip_address:
765-
IpRule = cmd.get_models('IPRule')
766-
if not rules.ip_rules:
767-
rules.ip_rules = []
768-
for ip in ip_address:
769-
to_modify = True
770-
for x in rules.ip_rules:
771-
existing_ip_network = ip_network(x.ip_address_or_range)
772-
new_ip_network = ip_network(ip)
773-
if new_ip_network.overlaps(existing_ip_network):
774-
logger.warning("IP/CIDR %s overlaps with %s, which exists already. Not adding duplicates.",
775-
ip, x.ip_address_or_range)
776-
to_modify = False
777-
break
778-
if to_modify:
779-
rules.ip_rules.append(IpRule(ip_address_or_range=ip, action=action))
779+
rules.ip_rules = _process_add_ip(cmd, ip_address, rules.ip_rules, action=action, ipv6=False)
780+
781+
if ipv6_address:
782+
rules.ipv6_rules = _process_add_ip(cmd, ipv6_address, rules.ipv6_rules, action=action, ipv6=True)
783+
780784
if resource_id:
781785
ResourceAccessRule = cmd.get_models('ResourceAccessRule')
782786
if not rules.resource_access_rules:
@@ -790,7 +794,26 @@ def add_network_rule(cmd, client, resource_group_name, account_name, action='All
790794
return client.update(resource_group_name, account_name, params)
791795

792796

793-
def remove_network_rule(cmd, client, resource_group_name, account_name, ip_address=None, subnet=None,
797+
def _process_add_ip(cmd, ip_address, ip_rules, action, ipv6=False):
798+
IpRule = cmd.get_models('IPRule')
799+
if not ip_rules:
800+
ip_rules = []
801+
for ip in ip_address:
802+
to_modify = True
803+
for x in ip_rules:
804+
existing_ip_network = ip_network(x.ip_address_or_range)
805+
new_ip_network = ip_network(ip)
806+
if new_ip_network.overlaps(existing_ip_network):
807+
logger.warning("IP%s/CIDR %s overlaps with %s, which exists already. Not adding duplicates.",
808+
"v6" if ipv6 else "v4", ip, x.ip_address_or_range)
809+
to_modify = False
810+
break
811+
if to_modify:
812+
ip_rules.append(IpRule(ip_address_or_range=ip, action=action))
813+
return ip_rules
814+
815+
816+
def remove_network_rule(cmd, client, resource_group_name, account_name, ip_address=None, ipv6_address=None, subnet=None,
794817
vnet_name=None, tenant_id=None, resource_id=None): # pylint: disable=unused-argument
795818
sa = client.get_properties(resource_group_name, account_name)
796819
rules = sa.network_rule_set
@@ -802,6 +825,11 @@ def remove_network_rule(cmd, client, resource_group_name, account_name, ip_addre
802825
rules.ip_rules = list(filter(lambda x: all(ip_network(x.ip_address_or_range) != i for i in to_remove),
803826
rules.ip_rules))
804827

828+
if ipv6_address:
829+
to_remove = [ip_network(x) for x in ipv6_address]
830+
rules.ipv6_rules = list(filter(lambda x: all(ip_network(x.ip_address_or_range) != i for i in to_remove),
831+
rules.ipv6_rules))
832+
805833
if resource_id:
806834
rules.resource_access_rules = [x for x in rules.resource_access_rules if
807835
not (x.tenant_id == tenant_id and x.resource_id == resource_id)]

0 commit comments

Comments
 (0)