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# pylint: disable=too-many-lines
7
8import argparse
9import base64
10import socket
11import os
12
13from knack.util import CLIError
14from knack.log import get_logger
15
16from azure.cli.core.commands.validators import \
17    (validate_tags, get_default_location_from_resource_group)
18from azure.cli.core.commands.template_create import get_folded_parameter_validator
19from azure.cli.core.commands.client_factory import get_subscription_id, get_mgmt_service_client
20from azure.cli.core.commands.validators import validate_parameter_set
21from azure.cli.core.profiles import ResourceType
22from azure.cli.core.azclierror import RequiredArgumentMissingError
23
24logger = get_logger(__name__)
25
26
27def _resolve_api_version(rcf, resource_provider_namespace, parent_resource_path, resource_type):
28    """
29    This is copied from src/azure-cli/azure/cli/command_modules/resource/custom.py in Azure/azure-cli
30    """
31    from azure.cli.core.parser import IncorrectUsageError
32
33    provider = rcf.providers.get(resource_provider_namespace)
34
35    # If available, we will use parent resource's api-version
36    resource_type_str = (parent_resource_path.split('/')[0] if parent_resource_path else resource_type)
37
38    rt = [t for t in provider.resource_types if t.resource_type.lower() == resource_type_str.lower()]
39    if not rt:
40        raise IncorrectUsageError('Resource type {} not found.'.format(resource_type_str))
41    if len(rt) == 1 and rt[0].api_versions:
42        npv = [v for v in rt[0].api_versions if 'preview' not in v.lower()]
43        return npv[0] if npv else rt[0].api_versions[0]
44    raise IncorrectUsageError(
45        'API version is required and could not be resolved for resource {}'.format(resource_type))
46
47
48def get_asg_validator(loader, dest):
49    from msrestazure.tools import is_valid_resource_id, resource_id
50
51    ApplicationSecurityGroup = loader.get_models('ApplicationSecurityGroup')
52
53    def _validate_asg_name_or_id(cmd, namespace):
54        subscription_id = get_subscription_id(cmd.cli_ctx)
55        resource_group = namespace.resource_group_name
56        names_or_ids = getattr(namespace, dest)
57        ids = []
58
59        if names_or_ids == [""] or not names_or_ids:
60            return
61
62        for val in names_or_ids:
63            if not is_valid_resource_id(val):
64                val = resource_id(
65                    subscription=subscription_id,
66                    resource_group=resource_group,
67                    namespace='Microsoft.Network', type='applicationSecurityGroups',
68                    name=val
69                )
70            ids.append(ApplicationSecurityGroup(id=val))
71        setattr(namespace, dest, ids)
72
73    return _validate_asg_name_or_id
74
75
76def get_subscription_list_validator(dest, model_class):
77    def _validate_subscription_list(cmd, namespace):
78        val = getattr(namespace, dest, None)
79        if not val:
80            return
81        model = cmd.get_models(model_class)
82        setattr(namespace, dest, model(subscriptions=val))
83
84    return _validate_subscription_list
85
86
87def get_vnet_validator(dest):
88    from msrestazure.tools import is_valid_resource_id, resource_id
89
90    def _validate_vnet_name_or_id(cmd, namespace):
91        SubResource = cmd.get_models('SubResource')
92        subscription_id = get_subscription_id(cmd.cli_ctx)
93
94        resource_group = namespace.resource_group_name
95        names_or_ids = getattr(namespace, dest)
96        ids = []
97
98        if names_or_ids == [''] or not names_or_ids:
99            return
100
101        for val in names_or_ids:
102            if not is_valid_resource_id(val):
103                val = resource_id(
104                    subscription=subscription_id,
105                    resource_group=resource_group,
106                    namespace='Microsoft.Network', type='virtualNetworks',
107                    name=val
108                )
109            ids.append(SubResource(id=val))
110        setattr(namespace, dest, ids)
111
112    return _validate_vnet_name_or_id
113
114
115def _validate_vpn_gateway_generation(namespace):
116    if namespace.gateway_type != 'Vpn' and namespace.vpn_gateway_generation:
117        raise CLIError('vpn_gateway_generation should not be provided if gateway_type is not Vpn.')
118
119
120def validate_vpn_connection_name_or_id(cmd, namespace):
121    if namespace.vpn_connection_ids:
122        from msrestazure.tools import is_valid_resource_id, resource_id
123        for index, vpn_connection_id in enumerate(namespace.vpn_connection_ids):
124            if not is_valid_resource_id(vpn_connection_id):
125                namespace.vpn_connection_ids[index] = resource_id(
126                    subscription=get_subscription_id(cmd.cli_ctx),
127                    resource_group=namespace.resource_group_name,
128                    namespace='Microsoft.Network',
129                    type='connections',
130                    name=vpn_connection_id
131                )
132
133
134def validate_ddos_name_or_id(cmd, namespace):
135    if namespace.ddos_protection_plan:
136        from msrestazure.tools import is_valid_resource_id, resource_id
137        if not is_valid_resource_id(namespace.ddos_protection_plan):
138            namespace.ddos_protection_plan = resource_id(
139                subscription=get_subscription_id(cmd.cli_ctx),
140                resource_group=namespace.resource_group_name,
141                namespace='Microsoft.Network', type='ddosProtectionPlans',
142                name=namespace.ddos_protection_plan
143            )
144
145
146# pylint: disable=inconsistent-return-statements
147def dns_zone_name_type(value):
148    if value:
149        return value[:-1] if value[-1] == '.' else value
150
151
152def _generate_ag_subproperty_id(cli_ctx, namespace, child_type, child_name, subscription=None):
153    from msrestazure.tools import resource_id
154    return resource_id(
155        subscription=subscription or get_subscription_id(cli_ctx),
156        resource_group=namespace.resource_group_name,
157        namespace='Microsoft.Network',
158        type='applicationGateways',
159        name=namespace.application_gateway_name,
160        child_type_1=child_type,
161        child_name_1=child_name)
162
163
164def _generate_lb_subproperty_id(cli_ctx, namespace, child_type, child_name, subscription=None):
165    from msrestazure.tools import resource_id
166    return resource_id(
167        subscription=subscription or get_subscription_id(cli_ctx),
168        resource_group=namespace.resource_group_name,
169        namespace='Microsoft.Network',
170        type='loadBalancers',
171        name=namespace.load_balancer_name,
172        child_type_1=child_type,
173        child_name_1=child_name)
174
175
176def _generate_lb_id_list_from_names_or_ids(cli_ctx, namespace, prop, child_type):
177    from msrestazure.tools import is_valid_resource_id
178    raw = getattr(namespace, prop)
179    if not raw:
180        return
181    raw = raw if isinstance(raw, list) else [raw]
182    result = []
183    for item in raw:
184        if is_valid_resource_id(item):
185            result.append({'id': item})
186        else:
187            if not namespace.load_balancer_name:
188                raise CLIError('Unable to process {}. Please supply a well-formed ID or '
189                               '--lb-name.'.format(item))
190            result.append({'id': _generate_lb_subproperty_id(
191                cli_ctx, namespace, child_type, item)})
192    setattr(namespace, prop, result)
193
194
195def validate_address_pool_id_list(cmd, namespace):
196    _generate_lb_id_list_from_names_or_ids(
197        cmd.cli_ctx, namespace, 'load_balancer_backend_address_pool_ids', 'backendAddressPools')
198
199
200def validate_address_pool_name_or_id(cmd, namespace):
201    from msrestazure.tools import is_valid_resource_id, parse_resource_id
202    address_pool = namespace.backend_address_pool
203    lb_name = namespace.load_balancer_name
204    gateway_name = namespace.application_gateway_name
205
206    usage_error = CLIError('usage error: --address-pool ID | --lb-name NAME --address-pool NAME '
207                           '| --gateway-name NAME --address-pool NAME')
208
209    if is_valid_resource_id(address_pool):
210        if lb_name or gateway_name:
211            raise usage_error
212        parts = parse_resource_id(address_pool)
213        if parts['type'] == 'loadBalancers':
214            namespace.load_balancer_name = parts['name']
215        elif parts['type'] == 'applicationGateways':
216            namespace.application_gateway_name = parts['name']
217        else:
218            raise usage_error
219    else:
220        if bool(lb_name) == bool(gateway_name):
221            raise usage_error
222
223        if lb_name:
224            namespace.backend_address_pool = _generate_lb_subproperty_id(
225                cmd.cli_ctx, namespace, 'backendAddressPools', address_pool)
226        elif gateway_name:
227            namespace.backend_address_pool = _generate_ag_subproperty_id(
228                cmd.cli_ctx, namespace, 'backendAddressPools', address_pool)
229
230
231def validate_address_prefixes(namespace):
232    if namespace.subnet_type != 'new':
233        validate_parameter_set(namespace,
234                               required=[],
235                               forbidden=['subnet_address_prefix', 'vnet_address_prefix'],
236                               description='existing subnet')
237
238
239def read_base_64_file(filename):
240    with open(filename, 'rb') as f:
241        contents = f.read()
242        base64_data = base64.b64encode(contents)
243        try:
244            return base64_data.decode('utf-8')
245        except UnicodeDecodeError:
246            return str(base64_data)
247
248
249def validate_cert(namespace):
250    if namespace.cert_data:
251        namespace.cert_data = read_base_64_file(namespace.cert_data)
252
253
254def validate_trusted_client_cert(namespace):
255    if namespace.client_cert_data is None or namespace.client_cert_name is None:
256        raise RequiredArgumentMissingError('To use this cmd, you must specify both name and data')
257    namespace.client_cert_data = read_base_64_file(namespace.client_cert_data)
258
259
260def validate_ssl_cert(namespace):
261    params = [namespace.cert_data, namespace.cert_password]
262    if all(not x for x in params) and not namespace.key_vault_secret_id:
263        # no cert supplied -- use HTTP
264        if not namespace.frontend_port:
265            namespace.frontend_port = 80
266    else:
267        if namespace.key_vault_secret_id:
268            return
269        # cert supplied -- use HTTPS
270        if not namespace.cert_data:
271            raise CLIError(
272                None, 'To use SSL certificate, you must specify both the filename')
273
274        # extract the certificate data from the provided file
275        namespace.cert_data = read_base_64_file(namespace.cert_data)
276
277        try:
278            # change default to frontend port 443 for https
279            if not namespace.frontend_port:
280                namespace.frontend_port = 443
281        except AttributeError:
282            # app-gateway ssl-cert create does not have these fields and that is okay
283            pass
284
285
286def validate_delegations(cmd, namespace):
287    if namespace.delegations:
288        Delegation = cmd.get_models('Delegation')
289        delegations = []
290        for i, item in enumerate(namespace.delegations):
291            if '/' not in item and len(item.split('.')) == 3:
292                # convert names to serviceNames
293                _, service, resource_type = item.split('.')
294                item = 'Microsoft.{}/{}'.format(service, resource_type)
295            delegations.append(Delegation(name=str(i), service_name=item))
296        namespace.delegations = delegations
297
298
299def validate_dns_record_type(namespace):
300    tokens = namespace.command.split(' ')
301    types = ['a', 'aaaa', 'caa', 'cname', 'mx', 'ns', 'ptr', 'soa', 'srv', 'txt']
302    for token in tokens:
303        if token in types:
304            if hasattr(namespace, 'record_type'):
305                namespace.record_type = token
306            else:
307                namespace.record_set_type = token
308            return
309
310
311def validate_user_assigned_identity(cmd, namespace):
312    from msrestazure.tools import is_valid_resource_id, resource_id
313
314    if namespace.user_assigned_identity and not is_valid_resource_id(namespace.user_assigned_identity):
315        namespace.user_assigned_identity = resource_id(
316            subscription=get_subscription_id(cmd.cli_ctx),
317            resource_group=namespace.resource_group_name,
318            namespace='Microsoft.ManagedIdentity',
319            type='userAssignedIdentities',
320            name=namespace.user_assigned_identity
321        )
322
323
324def validate_express_route_peering(cmd, namespace):
325    from msrestazure.tools import is_valid_resource_id, resource_id
326    circuit = namespace.circuit_name
327    peering = namespace.peering
328
329    if not circuit and not peering:
330        return
331
332    usage_error = CLIError('usage error: --peering ID | --peering NAME --circuit-name CIRCUIT')
333    if not is_valid_resource_id(peering):
334        namespace.peering = resource_id(
335            subscription=get_subscription_id(cmd.cli_ctx),
336            resource_group=namespace.resource_group_name,
337            namespace='Microsoft.Network',
338            type='expressRouteCircuits',
339            name=circuit,
340            child_type_1='peerings',
341            child_name_1=peering
342        )
343    elif circuit:
344        raise usage_error
345
346
347def validate_express_route_port(cmd, namespace):
348    from msrestazure.tools import is_valid_resource_id, resource_id
349    if namespace.express_route_port and not is_valid_resource_id(namespace.express_route_port):
350        namespace.express_route_port = resource_id(
351            subscription=get_subscription_id(cmd.cli_ctx),
352            resource_group=namespace.resource_group_name,
353            namespace='Microsoft.Network',
354            type='expressRoutePorts',
355            name=namespace.express_route_port
356        )
357
358
359def validate_virtul_network_gateway(cmd, namespace):
360    from msrestazure.tools import is_valid_resource_id, resource_id
361    if namespace.hosted_gateway and not is_valid_resource_id(namespace.hosted_gateway):
362        namespace.hosted_gateway = resource_id(
363            subscription=get_subscription_id(cmd.cli_ctx),
364            resource_group=namespace.resource_group_name,
365            namespace='Microsoft.Network',
366            type='virtualNetworkGateways',
367            name=namespace.hosted_gateway
368        )
369
370
371def validate_virtual_hub(cmd, namespace):
372    from msrestazure.tools import is_valid_resource_id, resource_id
373    if namespace.virtual_hub and not is_valid_resource_id(namespace.virtual_hub):
374        namespace.virtual_hub = resource_id(
375            subscription=get_subscription_id(cmd.cli_ctx),
376            resource_group=namespace.resource_group_name,
377            namespace='Microsoft.Network',
378            type='virtualHubs',
379            name=namespace.virtual_hub
380        )
381
382
383def validate_waf_policy(cmd, namespace):
384    from msrestazure.tools import is_valid_resource_id, resource_id
385    if namespace.firewall_policy and not is_valid_resource_id(namespace.firewall_policy):
386        namespace.firewall_policy = resource_id(
387            subscription=get_subscription_id(cmd.cli_ctx),
388            resource_group=namespace.resource_group_name,
389            namespace='Microsoft.Network',
390            type='ApplicationGatewayWebApplicationFirewallPolicies',
391            name=namespace.firewall_policy
392        )
393
394
395def bandwidth_validator_factory(mbps=True):
396    def validator(namespace):
397        return validate_circuit_bandwidth(namespace, mbps=mbps)
398
399    return validator
400
401
402def validate_circuit_bandwidth(namespace, mbps=True):
403    # use gbps if mbps is False
404    unit = 'mbps' if mbps else 'gbps'
405    bandwidth = None
406    bandwidth = getattr(namespace, 'bandwidth_in_{}'.format(unit), None)
407    if bandwidth is None:
408        return
409
410    if len(bandwidth) == 1:
411        bandwidth_comps = bandwidth[0].split(' ')
412    else:
413        bandwidth_comps = bandwidth
414
415    usage_error = CLIError('usage error: --bandwidth INT {Mbps,Gbps}')
416    if len(bandwidth_comps) == 1:
417        logger.warning('interpretting --bandwidth as %s. Consider being explicit: Mbps, Gbps', unit)
418        setattr(namespace, 'bandwidth_in_{}'.format(unit), float(bandwidth_comps[0]))
419        return
420    if len(bandwidth_comps) > 2:
421        raise usage_error
422
423    if float(bandwidth_comps[0]) and bandwidth_comps[1].lower() in ['mbps', 'gbps']:
424        input_unit = bandwidth_comps[1].lower()
425        if input_unit == unit:
426            converted_bandwidth = float(bandwidth_comps[0])
427        elif input_unit == 'gbps':
428            converted_bandwidth = float(bandwidth_comps[0]) * 1000
429        else:
430            converted_bandwidth = float(bandwidth_comps[0]) / 1000
431        setattr(namespace, 'bandwidth_in_{}'.format(unit), converted_bandwidth)
432    else:
433        raise usage_error
434
435
436def validate_er_peer_circuit(cmd, namespace):
437    from msrestazure.tools import resource_id, is_valid_resource_id
438
439    if not is_valid_resource_id(namespace.peer_circuit):
440        peer_id = resource_id(
441            subscription=get_subscription_id(cmd.cli_ctx),
442            resource_group=namespace.resource_group_name,
443            namespace='Microsoft.Network',
444            type='expressRouteCircuits',
445            name=namespace.peer_circuit,
446            child_type_1='peerings',
447            child_name_1=namespace.peering_name)
448    else:
449        peer_id = namespace.peer_circuit
450
451    # if the circuit ID is provided, we need to append /peerings/{peering_name}
452    if namespace.peering_name not in peer_id:
453        peer_id = '{}/peerings/{}'.format(peer_id, namespace.peering_name)
454
455    namespace.peer_circuit = peer_id
456
457
458def validate_inbound_nat_rule_id_list(cmd, namespace):
459    _generate_lb_id_list_from_names_or_ids(
460        cmd.cli_ctx, namespace, 'load_balancer_inbound_nat_rule_ids', 'inboundNatRules')
461
462
463def validate_inbound_nat_rule_name_or_id(cmd, namespace):
464    from msrestazure.tools import is_valid_resource_id
465    rule_name = namespace.inbound_nat_rule
466    lb_name = namespace.load_balancer_name
467
468    if is_valid_resource_id(rule_name):
469        if lb_name:
470            raise CLIError('Please omit --lb-name when specifying an inbound NAT rule ID.')
471    else:
472        if not lb_name:
473            raise CLIError('Please specify --lb-name when specifying an inbound NAT rule name.')
474        namespace.inbound_nat_rule = _generate_lb_subproperty_id(
475            cmd.cli_ctx, namespace, 'inboundNatRules', rule_name)
476
477
478def validate_ip_tags(cmd, namespace):
479    ''' Extracts multiple space-separated tags in TYPE=VALUE format '''
480    IpTag = cmd.get_models('IpTag')
481    if namespace.ip_tags and IpTag:
482        ip_tags = []
483        for item in namespace.ip_tags:
484            tag_type, tag_value = item.split('=', 1)
485            ip_tags.append(IpTag(ip_tag_type=tag_type, tag=tag_value))
486        namespace.ip_tags = ip_tags
487
488
489def validate_frontend_ip_configs(cmd, namespace):
490    from msrestazure.tools import is_valid_resource_id
491    if namespace.frontend_ip_configurations:
492        config_ids = []
493        for item in namespace.frontend_ip_configurations:
494            if not is_valid_resource_id(item):
495                config_ids.append(_generate_lb_subproperty_id(
496                    cmd.cli_ctx, namespace, 'frontendIpConfigurations', item))
497            else:
498                config_ids.append(item)
499        namespace.frontend_ip_configurations = config_ids
500
501
502def validate_local_gateway(cmd, namespace):
503    from msrestazure.tools import is_valid_resource_id, resource_id
504    if namespace.gateway_default_site and not is_valid_resource_id(namespace.gateway_default_site):
505        namespace.gateway_default_site = resource_id(
506            subscription=get_subscription_id(cmd.cli_ctx),
507            resource_group=namespace.resource_group_name,
508            name=namespace.gateway_default_site,
509            namespace='Microsoft.Network',
510            type='localNetworkGateways')
511
512
513def validate_match_variables(cmd, namespace):
514    if not namespace.match_variables:
515        return
516
517    MatchVariable = cmd.get_models('MatchVariable')
518    variables = []
519    for match in namespace.match_variables:
520        try:
521            name, selector = match.split('.', 1)
522        except ValueError:
523            name = match
524            selector = None
525        variables.append(MatchVariable(variable_name=name, selector=selector))
526    namespace.match_variables = variables
527
528
529def validate_metadata(namespace):
530    if namespace.metadata:
531        namespace.metadata = dict(x.split('=', 1) for x in namespace.metadata)
532
533
534def validate_peering_type(namespace):
535    if namespace.peering_type and namespace.peering_type == 'MicrosoftPeering':
536
537        if not namespace.advertised_public_prefixes:
538            raise CLIError(
539                'missing required MicrosoftPeering parameter --advertised-public-prefixes')
540
541
542def validate_public_ip_prefix(cmd, namespace):
543    from msrestazure.tools import is_valid_resource_id, resource_id
544    if namespace.public_ip_prefix and not is_valid_resource_id(namespace.public_ip_prefix):
545        namespace.public_ip_prefix = resource_id(
546            subscription=get_subscription_id(cmd.cli_ctx),
547            resource_group=namespace.resource_group_name,
548            name=namespace.public_ip_prefix,
549            namespace='Microsoft.Network',
550            type='publicIPPrefixes')
551
552
553def validate_nat_gateway(cmd, namespace):
554    from msrestazure.tools import is_valid_resource_id, resource_id
555    if namespace.nat_gateway and not is_valid_resource_id(namespace.nat_gateway):
556        namespace.nat_gateway = resource_id(
557            subscription=get_subscription_id(cmd.cli_ctx),
558            resource_group=namespace.resource_group_name,
559            name=namespace.nat_gateway,
560            namespace='Microsoft.Network',
561            type='natGateways')
562
563
564def validate_private_ip_address(namespace):
565    if namespace.private_ip_address and hasattr(namespace, 'private_ip_address_allocation'):
566        namespace.private_ip_address_allocation = 'static'
567
568
569def validate_route_filter(cmd, namespace):
570    from msrestazure.tools import is_valid_resource_id, resource_id
571    if namespace.route_filter:
572        if not is_valid_resource_id(namespace.route_filter):
573            namespace.route_filter = resource_id(
574                subscription=get_subscription_id(cmd.cli_ctx),
575                resource_group=namespace.resource_group_name,
576                namespace='Microsoft.Network',
577                type='routeFilters',
578                name=namespace.route_filter)
579
580
581def get_public_ip_validator(has_type_field=False, allow_none=False, allow_new=False,
582                            default_none=False):
583    """ Retrieves a validator for public IP address. Accepting all defaults will perform a check
584    for an existing name or ID with no ARM-required -type parameter. """
585    from msrestazure.tools import is_valid_resource_id, resource_id
586
587    def simple_validator(cmd, namespace):
588        if namespace.public_ip_address:
589            is_list = isinstance(namespace.public_ip_address, list)
590
591            def _validate_name_or_id(public_ip):
592                # determine if public_ip_address is name or ID
593                is_id = is_valid_resource_id(public_ip)
594                return public_ip if is_id else resource_id(
595                    subscription=get_subscription_id(cmd.cli_ctx),
596                    resource_group=namespace.resource_group_name,
597                    namespace='Microsoft.Network',
598                    type='publicIPAddresses',
599                    name=public_ip)
600
601            if is_list:
602                for i, public_ip in enumerate(namespace.public_ip_address):
603                    namespace.public_ip_address[i] = _validate_name_or_id(public_ip)
604            else:
605                namespace.public_ip_address = _validate_name_or_id(namespace.public_ip_address)
606
607    def complex_validator_with_type(cmd, namespace):
608        get_folded_parameter_validator(
609            'public_ip_address', 'Microsoft.Network/publicIPAddresses', '--public-ip-address',
610            allow_none=allow_none, allow_new=allow_new, default_none=default_none)(cmd, namespace)
611
612    return complex_validator_with_type if has_type_field else simple_validator
613
614
615def get_subnet_validator(has_type_field=False, allow_none=False, allow_new=False,
616                         default_none=False):
617    from msrestazure.tools import is_valid_resource_id, resource_id
618
619    def simple_validator(cmd, namespace):
620        if namespace.virtual_network_name is None and namespace.subnet is None:
621            return
622        if namespace.subnet == '':
623            return
624        usage_error = ValueError('incorrect usage: ( --subnet ID | --subnet NAME --vnet-name NAME)')
625        # error if vnet-name is provided without subnet
626        if namespace.virtual_network_name and not namespace.subnet:
627            raise usage_error
628
629        # determine if subnet is name or ID
630        is_id = is_valid_resource_id(namespace.subnet)
631
632        # error if vnet-name is provided along with a subnet ID
633        if is_id and namespace.virtual_network_name:
634            raise usage_error
635        if not is_id and not namespace.virtual_network_name:
636            raise usage_error
637
638        if not is_id:
639            namespace.subnet = resource_id(
640                subscription=get_subscription_id(cmd.cli_ctx),
641                resource_group=namespace.resource_group_name,
642                namespace='Microsoft.Network',
643                type='virtualNetworks',
644                name=namespace.virtual_network_name,
645                child_type_1='subnets',
646                child_name_1=namespace.subnet)
647
648    def complex_validator_with_type(cmd, namespace):
649
650        get_folded_parameter_validator(
651            'subnet', 'subnets', '--subnet',
652            'virtual_network_name', 'Microsoft.Network/virtualNetworks', '--vnet-name',
653            allow_none=allow_none, allow_new=allow_new, default_none=default_none)(cmd, namespace)
654
655    return complex_validator_with_type if has_type_field else simple_validator
656
657
658def get_nsg_validator(has_type_field=False, allow_none=False, allow_new=False, default_none=False):
659    from msrestazure.tools import is_valid_resource_id, resource_id
660
661    def simple_validator(cmd, namespace):
662        if namespace.network_security_group:
663            # determine if network_security_group is name or ID
664            is_id = is_valid_resource_id(namespace.network_security_group)
665            if not is_id:
666                namespace.network_security_group = resource_id(
667                    subscription=get_subscription_id(cmd.cli_ctx),
668                    resource_group=namespace.resource_group_name,
669                    namespace='Microsoft.Network',
670                    type='networkSecurityGroups',
671                    name=namespace.network_security_group)
672
673    def complex_validator_with_type(cmd, namespace):
674        get_folded_parameter_validator(
675            'network_security_group', 'Microsoft.Network/networkSecurityGroups', '--nsg',
676            allow_none=allow_none, allow_new=allow_new, default_none=default_none)(cmd, namespace)
677
678    return complex_validator_with_type if has_type_field else simple_validator
679
680
681def validate_service_endpoint_policy(cmd, namespace):
682    from msrestazure.tools import is_valid_resource_id, resource_id
683    if namespace.service_endpoint_policy:
684        policy_ids = []
685        for policy in namespace.service_endpoint_policy:
686            if not is_valid_resource_id(policy):
687                policy = resource_id(
688                    subscription=get_subscription_id(cmd.cli_ctx),
689                    resource_group=namespace.resource_group_name,
690                    name=policy,
691                    namespace='Microsoft.Network',
692                    type='serviceEndpointPolicies')
693            policy_ids.append(policy)
694        namespace.service_endpoint_policy = policy_ids
695
696
697def get_servers_validator(camel_case=False):
698    def validate_servers(namespace):
699        servers = []
700        for item in namespace.servers if namespace.servers else []:
701            try:
702                socket.inet_aton(item)  # pylint:disable=no-member
703                servers.append({'ipAddress' if camel_case else 'ip_address': item})
704            except socket.error:  # pylint:disable=no-member
705                servers.append({'fqdn': item})
706        namespace.servers = servers if servers else None
707
708    return validate_servers
709
710
711def validate_subresource_list(cmd, namespace):
712    if namespace.target_resources:
713        SubResource = cmd.get_models('SubResource')
714        subresources = []
715        for item in namespace.target_resources:
716            subresources.append(SubResource(id=item))
717        namespace.target_resources = subresources
718
719
720def validate_target_listener(cmd, namespace):
721    from msrestazure.tools import is_valid_resource_id, resource_id
722    if namespace.target_listener and not is_valid_resource_id(namespace.target_listener):
723        namespace.target_listener = resource_id(
724            subscription=get_subscription_id(cmd.cli_ctx),
725            resource_group=namespace.resource_group_name,
726            name=namespace.application_gateway_name,
727            namespace='Microsoft.Network',
728            type='applicationGateways',
729            child_type_1='httpListeners',
730            child_name_1=namespace.target_listener)
731
732
733def validate_private_dns_zone(cmd, namespace):
734    from msrestazure.tools import is_valid_resource_id, resource_id
735    if namespace.private_dns_zone and not is_valid_resource_id(namespace.private_dns_zone):
736        namespace.private_dns_zone = resource_id(
737            subscription=get_subscription_id(cmd.cli_ctx),
738            resource_group=namespace.resource_group_name,
739            name=namespace.private_dns_zone,
740            namespace='Microsoft.Network',
741            type='privateDnsZones')
742
743
744def get_virtual_network_validator(has_type_field=False, allow_none=False, allow_new=False,
745                                  default_none=False):
746    from msrestazure.tools import is_valid_resource_id, resource_id
747
748    def simple_validator(cmd, namespace):
749        if namespace.virtual_network:
750            # determine if vnet is name or ID
751            is_id = is_valid_resource_id(namespace.virtual_network)
752            if not is_id:
753                namespace.virtual_network = resource_id(
754                    subscription=get_subscription_id(cmd.cli_ctx),
755                    resource_group=namespace.resource_group_name,
756                    namespace='Microsoft.Network',
757                    type='virtualNetworks',
758                    name=namespace.virtual_network)
759
760    def complex_validator_with_type(cmd, namespace):
761        get_folded_parameter_validator(
762            'virtual_network', 'Microsoft.Network/virtualNetworks', '--vnet',
763            allow_none=allow_none, allow_new=allow_new, default_none=default_none)(cmd, namespace)
764
765    return complex_validator_with_type if has_type_field else simple_validator
766
767
768# COMMAND NAMESPACE VALIDATORS
769
770def process_ag_listener_create_namespace(cmd, namespace):  # pylint: disable=unused-argument
771    from msrestazure.tools import is_valid_resource_id, resource_id
772    if namespace.frontend_ip and not is_valid_resource_id(namespace.frontend_ip):
773        namespace.frontend_ip = _generate_ag_subproperty_id(
774            cmd.cli_ctx, namespace, 'frontendIpConfigurations', namespace.frontend_ip)
775
776    if namespace.frontend_port and not is_valid_resource_id(namespace.frontend_port):
777        namespace.frontend_port = _generate_ag_subproperty_id(
778            cmd.cli_ctx, namespace, 'frontendPorts', namespace.frontend_port)
779
780    if namespace.ssl_cert and not is_valid_resource_id(namespace.ssl_cert):
781        namespace.ssl_cert = _generate_ag_subproperty_id(
782            cmd.cli_ctx, namespace, 'sslCertificates', namespace.ssl_cert)
783
784    if namespace.firewall_policy and not is_valid_resource_id(namespace.firewall_policy):
785        namespace.firewall_policy = resource_id(
786            subscription=get_subscription_id(cmd.cli_ctx),
787            resource_group=namespace.resource_group_name,
788            namespace='Microsoft.Network',
789            type='ApplicationGatewayWebApplicationFirewallPolicies',
790            name=namespace.firewall_policy
791        )
792
793
794def process_ag_http_settings_create_namespace(cmd, namespace):  # pylint: disable=unused-argument
795    from msrestazure.tools import is_valid_resource_id
796    if namespace.probe and not is_valid_resource_id(namespace.probe):
797        namespace.probe = _generate_ag_subproperty_id(
798            cmd.cli_ctx, namespace, 'probes', namespace.probe)
799    if namespace.auth_certs:
800        def _validate_name_or_id(val):
801            return val if is_valid_resource_id(val) else _generate_ag_subproperty_id(
802                cmd.cli_ctx, namespace, 'authenticationCertificates', val)
803
804        namespace.auth_certs = [_validate_name_or_id(x) for x in namespace.auth_certs]
805    if namespace.root_certs:
806        def _validate_name_or_id(val):
807            return val if is_valid_resource_id(val) else _generate_ag_subproperty_id(
808                cmd.cli_ctx, namespace, 'trustedRootCertificates', val)
809
810        namespace.root_certs = [_validate_name_or_id(x) for x in namespace.root_certs]
811
812
813def process_ag_rule_create_namespace(cmd, namespace):  # pylint: disable=unused-argument
814    from msrestazure.tools import is_valid_resource_id
815    if namespace.address_pool and not is_valid_resource_id(namespace.address_pool):
816        namespace.address_pool = _generate_ag_subproperty_id(
817            cmd.cli_ctx, namespace, 'backendAddressPools', namespace.address_pool)
818
819    if namespace.http_listener and not is_valid_resource_id(namespace.http_listener):
820        namespace.http_listener = _generate_ag_subproperty_id(
821            cmd.cli_ctx, namespace, 'httpListeners', namespace.http_listener)
822
823    if namespace.http_settings and not is_valid_resource_id(namespace.http_settings):
824        namespace.http_settings = _generate_ag_subproperty_id(
825            cmd.cli_ctx, namespace, 'backendHttpSettingsCollection', namespace.http_settings)
826
827    if namespace.url_path_map and not is_valid_resource_id(namespace.url_path_map):
828        namespace.url_path_map = _generate_ag_subproperty_id(
829            cmd.cli_ctx, namespace, 'urlPathMaps', namespace.url_path_map)
830
831    if namespace.redirect_config and not is_valid_resource_id(namespace.redirect_config):
832        namespace.redirect_config = _generate_ag_subproperty_id(
833            cmd.cli_ctx, namespace, 'redirectConfigurations', namespace.redirect_config)
834
835    if namespace.rewrite_rule_set and not is_valid_resource_id(namespace.rewrite_rule_set):
836        namespace.rewrite_rule_set = _generate_ag_subproperty_id(
837            cmd.cli_ctx, namespace, 'rewriteRuleSets', namespace.rewrite_rule_set)
838
839
840def process_ag_ssl_policy_set_namespace(namespace):
841    if namespace.disabled_ssl_protocols and getattr(namespace, 'clear', None):
842        raise ValueError('incorrect usage: --disabled-ssl-protocols PROTOCOL [...] | --clear')
843
844
845def process_ag_url_path_map_create_namespace(cmd, namespace):  # pylint: disable=unused-argument
846    from msrestazure.tools import is_valid_resource_id
847    if namespace.default_address_pool and not is_valid_resource_id(namespace.default_address_pool):
848        namespace.default_address_pool = _generate_ag_subproperty_id(
849            cmd.cli_ctx, namespace, 'backendAddressPools', namespace.default_address_pool)
850
851    if namespace.default_http_settings and not is_valid_resource_id(
852            namespace.default_http_settings):
853        namespace.default_http_settings = _generate_ag_subproperty_id(
854            cmd.cli_ctx, namespace, 'backendHttpSettingsCollection', namespace.default_http_settings)
855
856    if namespace.default_redirect_config and not is_valid_resource_id(
857            namespace.default_redirect_config):
858        namespace.default_redirect_config = _generate_ag_subproperty_id(
859            cmd.cli_ctx, namespace, 'redirectConfigurations', namespace.default_redirect_config)
860
861    if hasattr(namespace, 'firewall_policy') and \
862            namespace.firewall_policy and not is_valid_resource_id(namespace.firewall_policy):
863        namespace.firewall_policy = _generate_ag_subproperty_id(
864            cmd.cli_ctx, namespace, 'firewallPolicy', namespace.firewall_policy
865        )
866
867    if namespace.default_rewrite_rule_set and not is_valid_resource_id(namespace.default_rewrite_rule_set):
868        namespace.default_rewrite_rule_set = _generate_ag_subproperty_id(
869            cmd.cli_ctx, namespace, 'rewriteRuleSets', namespace.default_rewrite_rule_set)
870
871    if hasattr(namespace, 'rule_name'):
872        process_ag_url_path_map_rule_create_namespace(cmd, namespace)
873
874
875def process_ag_url_path_map_rule_create_namespace(cmd, namespace):  # pylint: disable=unused-argument
876    from msrestazure.tools import is_valid_resource_id
877    if namespace.address_pool and not is_valid_resource_id(namespace.address_pool):
878        namespace.address_pool = _generate_ag_subproperty_id(
879            cmd.cli_ctx, namespace, 'backendAddressPools', namespace.address_pool)
880
881    if namespace.http_settings and not is_valid_resource_id(namespace.http_settings):
882        namespace.http_settings = _generate_ag_subproperty_id(
883            cmd.cli_ctx, namespace, 'backendHttpSettingsCollection', namespace.http_settings)
884
885    if namespace.redirect_config and not is_valid_resource_id(
886            namespace.redirect_config):
887        namespace.redirect_config = _generate_ag_subproperty_id(
888            cmd.cli_ctx, namespace, 'redirectConfigurations', namespace.redirect_config)
889
890    if namespace.rewrite_rule_set and not is_valid_resource_id(namespace.rewrite_rule_set):
891        namespace.rewrite_rule_set = _generate_ag_subproperty_id(
892            cmd.cli_ctx, namespace, 'rewriteRuleSets', namespace.rewrite_rule_set)
893
894
895def process_ag_create_namespace(cmd, namespace):
896    get_default_location_from_resource_group(cmd, namespace)
897    get_servers_validator(camel_case=True)(namespace)
898
899    # process folded parameters
900    if namespace.subnet or namespace.virtual_network_name:
901        get_subnet_validator(has_type_field=True, allow_new=True)(cmd, namespace)
902    validate_address_prefixes(namespace)
903    if namespace.public_ip_address:
904        get_public_ip_validator(
905            has_type_field=True, allow_none=True, allow_new=True, default_none=True)(cmd, namespace)
906    validate_ssl_cert(namespace)
907    validate_tags(namespace)
908    validate_custom_error_pages(namespace)
909    validate_waf_policy(cmd, namespace)
910    validate_user_assigned_identity(cmd, namespace)
911
912
913def process_auth_create_namespace(cmd, namespace):
914    ExpressRouteCircuitAuthorization = cmd.get_models('ExpressRouteCircuitAuthorization')
915    namespace.authorization_parameters = ExpressRouteCircuitAuthorization()
916
917
918def process_lb_create_namespace(cmd, namespace):
919    get_default_location_from_resource_group(cmd, namespace)
920    validate_tags(namespace)
921
922    if namespace.subnet and namespace.public_ip_address:
923        raise ValueError(
924            'incorrect usage: --subnet NAME --vnet-name NAME | '
925            '--subnet ID | --public-ip-address NAME_OR_ID')
926
927    if namespace.subnet:
928        # validation for an internal load balancer
929        get_subnet_validator(
930            has_type_field=True, allow_new=True, allow_none=True, default_none=True)(cmd, namespace)
931
932        namespace.public_ip_address_type = None
933        namespace.public_ip_address = None
934
935    else:
936        # validation for internet facing load balancer
937        get_public_ip_validator(has_type_field=True, allow_none=True, allow_new=True)(cmd, namespace)
938
939        if namespace.public_ip_dns_name and namespace.public_ip_address_type != 'new':
940            raise CLIError(
941                'specify --public-ip-dns-name only if creating a new public IP address.')
942
943        namespace.subnet_type = None
944        namespace.subnet = None
945        namespace.virtual_network_name = None
946
947
948def process_lb_frontend_ip_namespace(cmd, namespace):
949    from msrestazure.tools import is_valid_resource_id, resource_id
950    if namespace.subnet and namespace.public_ip_address:
951        raise ValueError(
952            'incorrect usage: --subnet NAME --vnet-name NAME | '
953            '--subnet ID | --public-ip NAME_OR_ID')
954
955    if namespace.public_ip_prefix:
956        if not is_valid_resource_id(namespace.public_ip_prefix):
957            namespace.public_ip_prefix = resource_id(
958                subscription=get_subscription_id(cmd.cli_ctx),
959                resource_group=namespace.resource_group_name,
960                namespace='Microsoft.Network',
961                type='publicIpPrefixes',
962                name=namespace.public_ip_prefix)
963
964    if namespace.subnet:
965        get_subnet_validator()(cmd, namespace)
966    else:
967        get_public_ip_validator()(cmd, namespace)
968
969
970def process_cross_region_lb_create_namespace(cmd, namespace):
971    get_default_location_from_resource_group(cmd, namespace)
972    validate_tags(namespace)
973
974    # validation for internet facing load balancer
975    get_public_ip_validator(has_type_field=True, allow_none=True, allow_new=True)(cmd, namespace)
976
977    if namespace.public_ip_dns_name and namespace.public_ip_address_type != 'new':
978        raise CLIError(
979            'specify --public-ip-dns-name only if creating a new public IP address.')
980
981
982def process_cross_region_lb_frontend_ip_namespace(cmd, namespace):
983    from azure.mgmt.core.tools import is_valid_resource_id, resource_id
984
985    if namespace.public_ip_prefix:
986        if not is_valid_resource_id(namespace.public_ip_prefix):
987            namespace.public_ip_prefix = resource_id(
988                subscription=get_subscription_id(cmd.cli_ctx),
989                resource_group=namespace.resource_group_name,
990                namespace='Microsoft.Network',
991                type='publicIpPrefixes',
992                name=namespace.public_ip_prefix)
993
994    get_public_ip_validator()(cmd, namespace)
995
996
997def process_local_gateway_create_namespace(cmd, namespace):
998    ns = namespace
999    get_default_location_from_resource_group(cmd, ns)
1000    validate_tags(ns)
1001
1002    use_bgp_settings = any([ns.asn or ns.bgp_peering_address or ns.peer_weight])
1003    if use_bgp_settings and (not ns.asn or not ns.bgp_peering_address):
1004        raise ValueError(
1005            'incorrect usage: --bgp-peering-address IP --asn ASN [--peer-weight WEIGHT]')
1006
1007
1008def process_nic_create_namespace(cmd, namespace):
1009    get_default_location_from_resource_group(cmd, namespace)
1010    validate_tags(namespace)
1011
1012    validate_ag_address_pools(cmd, namespace)
1013    validate_address_pool_id_list(cmd, namespace)
1014    validate_inbound_nat_rule_id_list(cmd, namespace)
1015    get_asg_validator(cmd.loader, 'application_security_groups')(cmd, namespace)
1016
1017    # process folded parameters
1018    get_subnet_validator(has_type_field=False)(cmd, namespace)
1019    get_public_ip_validator(has_type_field=False, allow_none=True, default_none=True)(cmd, namespace)
1020    get_nsg_validator(has_type_field=False, allow_none=True, default_none=True)(cmd, namespace)
1021
1022
1023def process_public_ip_create_namespace(cmd, namespace):
1024    get_default_location_from_resource_group(cmd, namespace)
1025    validate_public_ip_prefix(cmd, namespace)
1026    validate_ip_tags(cmd, namespace)
1027    validate_tags(namespace)
1028    _inform_coming_breaking_change_for_public_ip(namespace)
1029
1030
1031def _inform_coming_breaking_change_for_public_ip(namespace):
1032    if namespace.sku == 'Standard' and not namespace.zone:
1033        logger.warning('[Coming breaking change] In the coming release, the default behavior will be changed as follows'
1034                       ' when sku is Standard and zone is not provided:'
1035                       ' For zonal regions, you will get a zone-redundant IP indicated by zones:["1","2","3"];'
1036                       ' For non-zonal regions, you will get a non zone-redundant IP indicated by zones:null.')
1037
1038
1039def process_route_table_create_namespace(cmd, namespace):
1040    get_default_location_from_resource_group(cmd, namespace)
1041    validate_tags(namespace)
1042
1043
1044def process_tm_endpoint_create_namespace(cmd, namespace):
1045    from azure.mgmt.trafficmanager import TrafficManagerManagementClient
1046
1047    client = get_mgmt_service_client(cmd.cli_ctx, TrafficManagerManagementClient).profiles
1048    profile = client.get(namespace.resource_group_name, namespace.profile_name)
1049
1050    routing_type = profile.traffic_routing_method  # pylint: disable=no-member
1051    endpoint_type = namespace.endpoint_type
1052    all_options = ['target_resource_id', 'target', 'min_child_endpoints', 'priority', 'weight', 'endpoint_location']
1053    props_to_options = {
1054        'target_resource_id': '--target-resource-id',
1055        'target': '--target',
1056        'min_child_endpoints': '--min-child-endpoints',
1057        'priority': '--priority',
1058        'weight': '--weight',
1059        'endpoint_location': '--endpoint-location',
1060        'geo_mapping': '--geo-mapping'
1061    }
1062    validate_subnet_ranges(namespace)
1063    validate_custom_headers(namespace)
1064
1065    required_options = []
1066
1067    # determine which options are required based on profile and routing method
1068    if endpoint_type.lower() == 'externalendpoints':
1069        required_options.append('target')
1070    else:
1071        required_options.append('target_resource_id')
1072
1073    if routing_type.lower() == 'weighted':
1074        required_options.append('weight')
1075    elif routing_type.lower() == 'priority':
1076        required_options.append('priority')
1077
1078    if endpoint_type.lower() == 'nestedendpoints':
1079        required_options.append('min_child_endpoints')
1080
1081    if endpoint_type.lower() in ['nestedendpoints', 'externalendpoints'] and routing_type.lower() == 'performance':
1082        required_options.append('endpoint_location')
1083
1084    if routing_type.lower() == 'geographic':
1085        required_options.append('geo_mapping')
1086
1087    # ensure required options are provided
1088    missing_options = [props_to_options[x] for x in required_options if getattr(namespace, x, None) is None]
1089    extra_options = [props_to_options[x] for x in all_options if
1090                     getattr(namespace, x, None) is not None and x not in required_options]
1091
1092    if missing_options or extra_options:
1093        error_message = "Incorrect options for profile routing method '{}' and endpoint type '{}'.".format(routing_type,
1094                                                                                                           endpoint_type)  # pylint: disable=line-too-long
1095        if missing_options:
1096            error_message = '{}\nSupply the following: {}'.format(error_message, ', '.join(
1097                missing_options))
1098        if extra_options:
1099            error_message = '{}\nOmit the following: {}'.format(error_message, ', '.join(
1100                extra_options))
1101        raise CLIError(error_message)
1102
1103
1104def process_vnet_create_namespace(cmd, namespace):
1105    get_default_location_from_resource_group(cmd, namespace)
1106    validate_ddos_name_or_id(cmd, namespace)
1107    validate_tags(namespace)
1108    get_nsg_validator()(cmd, namespace)
1109
1110    if namespace.subnet_prefix and not namespace.subnet_name:
1111        if cmd.supported_api_version(min_api='2018-08-01'):
1112            raise ValueError('incorrect usage: --subnet-name NAME [--subnet-prefixes PREFIXES]')
1113        raise ValueError('incorrect usage: --subnet-name NAME [--subnet-prefix PREFIX]')
1114
1115    if namespace.subnet_name and not namespace.subnet_prefix:
1116        if isinstance(namespace.vnet_prefixes, str):
1117            namespace.vnet_prefixes = [namespace.vnet_prefixes]
1118        prefix_components = namespace.vnet_prefixes[0].split('/', 1)
1119        address = prefix_components[0]
1120        bit_mask = int(prefix_components[1])
1121        subnet_mask = 24 if bit_mask < 24 else bit_mask
1122        subnet_prefix = '{}/{}'.format(address, subnet_mask)
1123        namespace.subnet_prefix = [subnet_prefix] if cmd.supported_api_version(min_api='2018-08-01') else subnet_prefix
1124
1125
1126def _validate_cert(namespace, param_name):
1127    attr = getattr(namespace, param_name)
1128    if attr and os.path.isfile(attr):
1129        setattr(namespace, param_name, read_base_64_file(attr))
1130
1131
1132def process_vnet_gateway_create_namespace(cmd, namespace):
1133    ns = namespace
1134    get_default_location_from_resource_group(cmd, ns)
1135    validate_tags(ns)
1136
1137    _validate_vpn_gateway_generation(ns)
1138
1139    get_virtual_network_validator()(cmd, ns)
1140
1141    get_public_ip_validator()(cmd, ns)
1142    public_ip_count = len(ns.public_ip_address or [])
1143    if public_ip_count > 2:
1144        raise CLIError('Specify a single public IP to create an active-standby gateway or two '
1145                       'public IPs to create an active-active gateway.')
1146
1147    validate_local_gateway(cmd, ns)
1148
1149    enable_bgp = any([ns.asn, ns.bgp_peering_address, ns.peer_weight])
1150    if enable_bgp and not ns.asn:
1151        raise ValueError(
1152            'incorrect usage: --asn ASN [--peer-weight WEIGHT --bgp-peering-address IP ]')
1153
1154    if cmd.supported_api_version(min_api='2020-11-01'):
1155        _validate_cert(namespace, 'root_cert_data')
1156
1157
1158def process_vnet_gateway_update_namespace(cmd, namespace):
1159    ns = namespace
1160    get_virtual_network_validator()(cmd, ns)
1161    get_public_ip_validator()(cmd, ns)
1162    validate_tags(ns)
1163    if cmd.supported_api_version(min_api='2020-11-01'):
1164        _validate_cert(namespace, 'root_cert_data')
1165    public_ip_count = len(ns.public_ip_address or [])
1166    if public_ip_count > 2:
1167        raise CLIError('Specify a single public IP to create an active-standby gateway or two '
1168                       'public IPs to create an active-active gateway.')
1169
1170
1171def process_vpn_connection_create_namespace(cmd, namespace):
1172    from msrestazure.tools import is_valid_resource_id, resource_id
1173    get_default_location_from_resource_group(cmd, namespace)
1174    validate_tags(namespace)
1175
1176    args = [a for a in [namespace.express_route_circuit2,
1177                        namespace.local_gateway2,
1178                        namespace.vnet_gateway2]
1179            if a]
1180    if len(args) != 1:
1181        raise ValueError('usage error: --vnet-gateway2 NAME_OR_ID | --local-gateway2 NAME_OR_ID '
1182                         '| --express-route-circuit2 NAME_OR_ID')
1183
1184    def _validate_name_or_id(value, resource_type):
1185        if not is_valid_resource_id(value):
1186            subscription = getattr(namespace, 'subscription', get_subscription_id(cmd.cli_ctx))
1187            return resource_id(
1188                subscription=subscription,
1189                resource_group=namespace.resource_group_name,
1190                namespace='Microsoft.Network',
1191                type=resource_type,
1192                name=value)
1193        return value
1194
1195    if (namespace.local_gateway2 or namespace.vnet_gateway2) and not namespace.shared_key:
1196        raise CLIError('--shared-key is required for VNET-to-VNET or Site-to-Site connections.')
1197
1198    if namespace.express_route_circuit2 and namespace.shared_key:
1199        raise CLIError('--shared-key cannot be used with an ExpressRoute connection.')
1200
1201    namespace.vnet_gateway1 = \
1202        _validate_name_or_id(namespace.vnet_gateway1, 'virtualNetworkGateways')
1203
1204    if namespace.express_route_circuit2:
1205        namespace.express_route_circuit2 = \
1206            _validate_name_or_id(
1207                namespace.express_route_circuit2, 'expressRouteCircuits')
1208        namespace.connection_type = 'ExpressRoute'
1209    elif namespace.local_gateway2:
1210        namespace.local_gateway2 = \
1211            _validate_name_or_id(namespace.local_gateway2, 'localNetworkGateways')
1212        namespace.connection_type = 'IPSec'
1213    elif namespace.vnet_gateway2:
1214        namespace.vnet_gateway2 = \
1215            _validate_name_or_id(namespace.vnet_gateway2, 'virtualNetworkGateways')
1216        namespace.connection_type = 'Vnet2Vnet'
1217
1218
1219def load_cert_file(param_name):
1220    def load_cert_validator(namespace):
1221        attr = getattr(namespace, param_name)
1222        if attr and os.path.isfile(attr):
1223            setattr(namespace, param_name, read_base_64_file(attr))
1224
1225    return load_cert_validator
1226
1227
1228def get_network_watcher_from_vm(cmd, namespace):
1229    from msrestazure.tools import parse_resource_id
1230
1231    compute_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_COMPUTE).virtual_machines
1232    vm_name = parse_resource_id(namespace.vm)['name']
1233    vm = compute_client.get(namespace.resource_group_name, vm_name)
1234    namespace.location = vm.location  # pylint: disable=no-member
1235    get_network_watcher_from_location()(cmd, namespace)
1236
1237
1238def get_network_watcher_from_resource(cmd, namespace):
1239    from azure.cli.core.commands.arm import get_arm_resource_by_id
1240    resource = get_arm_resource_by_id(cmd.cli_ctx, namespace.resource)
1241    namespace.location = resource.location  # pylint: disable=no-member
1242    get_network_watcher_from_location(remove=True)(cmd, namespace)
1243
1244
1245def get_network_watcher_from_location(remove=False, watcher_name='watcher_name',
1246                                      rg_name='watcher_rg'):
1247    def _validator(cmd, namespace):
1248        from msrestazure.tools import parse_resource_id
1249
1250        location = namespace.location
1251        network_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK).network_watchers
1252        watcher = next((x for x in network_client.list_all() if x.location.lower() == location.lower()), None)
1253        if not watcher:
1254            raise CLIError("network watcher is not enabled for region '{}'.".format(location))
1255        id_parts = parse_resource_id(watcher.id)
1256        setattr(namespace, rg_name, id_parts['resource_group'])
1257        setattr(namespace, watcher_name, id_parts['name'])
1258
1259        if remove:
1260            del namespace.location
1261
1262    return _validator
1263
1264
1265def process_nw_cm_v1_create_namespace(cmd, namespace):
1266    from msrestazure.tools import is_valid_resource_id, resource_id, parse_resource_id
1267
1268    validate_tags(namespace)
1269
1270    compute_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_COMPUTE).virtual_machines
1271    vm_name = parse_resource_id(namespace.source_resource)['name']
1272    rg = namespace.resource_group_name or parse_resource_id(namespace.source_resource).get('resource_group', None)
1273    if not rg:
1274        raise CLIError('usage error: --source-resource ID | --source-resource NAME --resource-group NAME')
1275    vm = compute_client.get(rg, vm_name)
1276    namespace.location = vm.location  # pylint: disable=no-member
1277    get_network_watcher_from_location()(cmd, namespace)
1278
1279    if namespace.source_resource and not is_valid_resource_id(namespace.source_resource):
1280        namespace.source_resource = resource_id(
1281            subscription=get_subscription_id(cmd.cli_ctx),
1282            resource_group=rg,
1283            namespace='Microsoft.Compute',
1284            type='virtualMachines',
1285            name=namespace.source_resource)
1286
1287    if namespace.dest_resource and not is_valid_resource_id(namespace.dest_resource):
1288        namespace.dest_resource = resource_id(
1289            subscription=get_subscription_id(cmd.cli_ctx),
1290            resource_group=namespace.resource_group_name,
1291            namespace='Microsoft.Compute',
1292            type='virtualMachines',
1293            name=namespace.dest_resource)
1294
1295
1296def process_nw_cm_v2_create_namespace(cmd, namespace):
1297    if namespace.location is None:  # location is None only occurs in creating a V2 connection monitor
1298        endpoint_source_resource_id = namespace.endpoint_source_resource_id
1299
1300        from msrestazure.tools import is_valid_resource_id, parse_resource_id
1301        from azure.mgmt.resource import ResourceManagementClient
1302
1303        # parse and verify endpoint_source_resource_id
1304        if endpoint_source_resource_id is None:
1305            raise CLIError('usage error: '
1306                           '--location/--endpoint-source-resource-id is required to create a V2 connection monitor')
1307        if is_valid_resource_id(endpoint_source_resource_id) is False:
1308            raise CLIError('usage error: "{}" is not a valid resource id'.format(endpoint_source_resource_id))
1309
1310        resource = parse_resource_id(namespace.endpoint_source_resource_id)
1311        resource_client = get_mgmt_service_client(cmd.cli_ctx, ResourceManagementClient)
1312        resource_api_version = _resolve_api_version(resource_client,
1313                                                    resource['namespace'],
1314                                                    resource['resource_parent'],
1315                                                    resource['resource_type'])
1316        resource = resource_client.resources.get_by_id(namespace.endpoint_source_resource_id, resource_api_version)
1317
1318        namespace.location = resource.location
1319        if namespace.location is None:
1320            raise CLIError("Can not get location from --endpoint-source-resource-id")
1321
1322    v2_required_parameter_set = [
1323        'endpoint_source_resource_id', 'endpoint_source_name', 'endpoint_dest_name', 'test_config_name'
1324    ]
1325    for p in v2_required_parameter_set:
1326        if not hasattr(namespace, p) or getattr(namespace, p) is None:
1327            raise CLIError(
1328                'usage error: --{} is required to create a V2 connection monitor'.format(p.replace('_', '-')))
1329    if namespace.test_config_protocol is None:
1330        raise CLIError('usage error: --protocol is required to create a test configuration for V2 connection monitor')
1331
1332    v2_optional_parameter_set = ['workspace_ids']
1333    if namespace.output_type is not None:
1334        tmp = [p for p in v2_optional_parameter_set if getattr(namespace, p) is None]
1335        if v2_optional_parameter_set == tmp:
1336            raise CLIError('usage error: --output-type is specified but no other resource id provided')
1337
1338    return get_network_watcher_from_location()(cmd, namespace)
1339
1340
1341def process_nw_cm_create_namespace(cmd, namespace):
1342    # V2 parameter set
1343    if namespace.source_resource is None:
1344        return process_nw_cm_v2_create_namespace(cmd, namespace)
1345
1346    # V1 parameter set
1347    return process_nw_cm_v1_create_namespace(cmd, namespace)
1348
1349
1350def process_nw_cm_v2_endpoint_namespace(cmd, namespace):
1351    if hasattr(namespace, 'filter_type') or hasattr(namespace, 'filter_items'):
1352        filter_type, filter_items = namespace.filter_type, namespace.filter_items
1353        if (filter_type and not filter_items) or (not filter_type and filter_items):
1354            raise CLIError('usage error: --filter-type and --filter-item must be present at the same time.')
1355
1356    if hasattr(namespace, 'dest_test_groups') or hasattr(namespace, 'source_test_groups'):
1357        dest_test_groups, source_test_groups = namespace.dest_test_groups, namespace.source_test_groups
1358        if dest_test_groups is None and source_test_groups is None:
1359            raise CLIError('usage error: endpoint has to be referenced from at least one existing test group '
1360                           'via --dest-test-groups/--source-test-groups')
1361
1362    return get_network_watcher_from_location()(cmd, namespace)
1363
1364
1365def process_nw_cm_v2_test_configuration_namespace(cmd, namespace):
1366    return get_network_watcher_from_location()(cmd, namespace)
1367
1368
1369def process_nw_cm_v2_test_group(cmd, namespace):
1370    return get_network_watcher_from_location()(cmd, namespace)
1371
1372
1373def process_nw_cm_v2_output_namespace(cmd, namespace):
1374    v2_output_optional_parameter_set = ['workspace_id']
1375    if hasattr(namespace, 'out_type') and namespace.out_type is not None:
1376        tmp = [p for p in v2_output_optional_parameter_set if getattr(namespace, p) is None]
1377        if v2_output_optional_parameter_set == tmp:
1378            raise CLIError('usage error: --type is specified but no other resource id provided')
1379
1380    return get_network_watcher_from_location()(cmd, namespace)
1381
1382
1383# pylint: disable=protected-access,too-few-public-methods
1384class NWConnectionMonitorEndpointFilterItemAction(argparse._AppendAction):
1385    def __call__(self, parser, namespace, values, option_string=None):
1386        ConnectionMonitorEndpointFilterItem = namespace._cmd.get_models('ConnectionMonitorEndpointFilterItem')
1387
1388        if not namespace.filter_items:
1389            namespace.filter_items = []
1390
1391        filter_item = ConnectionMonitorEndpointFilterItem()
1392
1393        for item in values:
1394            try:
1395                key, val = item.split('=', 1)
1396
1397                if hasattr(filter_item, key):
1398                    setattr(filter_item, key, val)
1399                else:
1400                    raise CLIError(
1401                        "usage error: '{}' is not a valid property of ConnectionMonitorEndpointFilterItem".format(key))
1402            except ValueError:
1403                raise CLIError(
1404                    'usage error: {} PropertyName=PropertyValue [PropertyName=PropertyValue ...]'.format(option_string))
1405
1406        namespace.filter_items.append(filter_item)
1407
1408
1409# pylint: disable=protected-access,too-few-public-methods
1410class NWConnectionMonitorTestConfigurationHTTPRequestHeaderAction(argparse._AppendAction):
1411    def __call__(self, parser, namespace, values, option_string=None):
1412        HTTPHeader = namespace._cmd.get_models('HTTPHeader')
1413
1414        if not namespace.http_request_headers:
1415            namespace.http_request_headers = []
1416
1417        request_header = HTTPHeader()
1418
1419        for item in values:
1420            try:
1421                key, val = item.split('=', 1)
1422                if hasattr(request_header, key):
1423                    setattr(request_header, key, val)
1424                else:
1425                    raise CLIError("usage error: '{}' is not a value property of HTTPHeader".format(key))
1426            except ValueError:
1427                raise CLIError(
1428                    'usage error: {} name=HTTPHeader value=HTTPHeaderValue'.format(option_string))
1429
1430        namespace.http_request_headers.append(request_header)
1431
1432
1433def process_nw_test_connectivity_namespace(cmd, namespace):
1434    from msrestazure.tools import is_valid_resource_id, resource_id, parse_resource_id
1435
1436    compute_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_COMPUTE).virtual_machines
1437    vm_name = parse_resource_id(namespace.source_resource)['name']
1438    rg = namespace.resource_group_name or parse_resource_id(namespace.source_resource).get('resource_group', None)
1439    if not rg:
1440        raise CLIError('usage error: --source-resource ID | --source-resource NAME --resource-group NAME')
1441    vm = compute_client.get(rg, vm_name)
1442    namespace.location = vm.location  # pylint: disable=no-member
1443    get_network_watcher_from_location(remove=True)(cmd, namespace)
1444
1445    if namespace.source_resource and not is_valid_resource_id(namespace.source_resource):
1446        namespace.source_resource = resource_id(
1447            subscription=get_subscription_id(cmd.cli_ctx),
1448            resource_group=rg,
1449            namespace='Microsoft.Compute',
1450            type='virtualMachines',
1451            name=namespace.source_resource)
1452
1453    if namespace.dest_resource and not is_valid_resource_id(namespace.dest_resource):
1454        namespace.dest_resource = resource_id(
1455            subscription=get_subscription_id(cmd.cli_ctx),
1456            resource_group=namespace.resource_group_name,
1457            namespace='Microsoft.Compute',
1458            type='virtualMachines',
1459            name=namespace.dest_resource)
1460
1461    if namespace.headers:
1462        HTTPHeader = cmd.get_models('HTTPHeader')
1463        headers = []
1464        for item in namespace.headers:
1465            parts = item.split('=')
1466            if len(parts) != 2:
1467                raise CLIError("usage error '{}': --headers KEY=VALUE [KEY=VALUE ...]".format(item))
1468            headers.append(HTTPHeader(name=parts[0], value=parts[1]))
1469        namespace.headers = headers
1470
1471
1472def process_nw_flow_log_create_namespace(cmd, namespace):
1473    """
1474    Flow Log is the sub-resource of Network Watcher, they must be in the same region and subscription.
1475    """
1476    from msrestazure.tools import is_valid_resource_id, resource_id
1477
1478    # for both create and update
1479    if namespace.resource_group_name is None:
1480        err_tpl, err_body = 'usage error: use {} instead.', None
1481
1482        if namespace.nsg and not is_valid_resource_id(namespace.nsg):
1483            err_body = '--nsg ID / --nsg NSD_NAME --resource-group NSD_RESOURCE_GROUP'
1484
1485        if namespace.storage_account and not is_valid_resource_id(namespace.storage_account):
1486            err_body = '--storage-account ID / --storage-account NAME --resource_group STORAGE_ACCOUNT_RESOURCE_GROUP'
1487
1488        if namespace.traffic_analytics_workspace and not is_valid_resource_id(namespace.traffic_analytics_workspace):
1489            err_body = '--workspace ID / --workspace NAME --resource-group WORKSPACE_RESOURCE_GROUP'
1490
1491        if err_body is not None:
1492            raise CLIError(err_tpl.format(err_body))
1493
1494    # for both create and update
1495    if namespace.nsg and not is_valid_resource_id(namespace.nsg):
1496        kwargs = {
1497            'subscription': get_subscription_id(cmd.cli_ctx),
1498            'resource_group': namespace.resource_group_name,
1499            'namespace': 'Microsoft.Network',
1500            'type': 'networkSecurityGroups',
1501            'name': namespace.nsg
1502        }
1503        namespace.nsg = resource_id(**kwargs)
1504
1505    # for both create and update
1506    if namespace.storage_account and not is_valid_resource_id(namespace.storage_account):
1507        kwargs = {
1508            'subscription': get_subscription_id(cmd.cli_ctx),
1509            'resource_group': namespace.resource_group_name,
1510            'namespace': 'Microsoft.Storage',
1511            'type': 'storageAccounts',
1512            'name': namespace.storage_account
1513        }
1514        namespace.storage_account = resource_id(**kwargs)
1515
1516    # for both create and update
1517    if namespace.traffic_analytics_workspace and not is_valid_resource_id(namespace.traffic_analytics_workspace):
1518        kwargs = {
1519            'subscription': get_subscription_id(cmd.cli_ctx),
1520            'resource_group': namespace.resource_group_name,
1521            'namespace': 'Microsoft.OperationalInsights',
1522            'type': 'workspaces',
1523            'name': namespace.traffic_analytics_workspace
1524        }
1525        namespace.traffic_analytics_workspace = resource_id(**kwargs)
1526
1527    get_network_watcher_from_location(remove=False)(cmd, namespace)
1528
1529    validate_tags(namespace)
1530
1531
1532def process_nw_flow_log_set_namespace(cmd, namespace):
1533    from msrestazure.tools import is_valid_resource_id, resource_id
1534    if namespace.storage_account and not is_valid_resource_id(namespace.storage_account):
1535        namespace.storage_account = resource_id(
1536            subscription=get_subscription_id(cmd.cli_ctx),
1537            resource_group=namespace.resource_group_name,
1538            namespace='Microsoft.Storage',
1539            type='storageAccounts',
1540            name=namespace.storage_account)
1541    if namespace.traffic_analytics_workspace and not is_valid_resource_id(namespace.traffic_analytics_workspace):
1542        namespace.traffic_analytics_workspace = resource_id(
1543            subscription=get_subscription_id(cmd.cli_ctx),
1544            resource_group=namespace.resource_group_name,
1545            namespace='Microsoft.OperationalInsights',
1546            type='workspaces',
1547            name=namespace.traffic_analytics_workspace)
1548
1549    process_nw_flow_log_show_namespace(cmd, namespace)
1550
1551
1552def process_nw_flow_log_show_namespace(cmd, namespace):
1553    from msrestazure.tools import is_valid_resource_id, resource_id
1554    from azure.cli.core.commands.arm import get_arm_resource_by_id
1555
1556    if hasattr(namespace, 'nsg') and namespace.nsg is not None:
1557        if not is_valid_resource_id(namespace.nsg):
1558            namespace.nsg = resource_id(
1559                subscription=get_subscription_id(cmd.cli_ctx),
1560                resource_group=namespace.resource_group_name,
1561                namespace='Microsoft.Network',
1562                type='networkSecurityGroups',
1563                name=namespace.nsg)
1564
1565        nsg = get_arm_resource_by_id(cmd.cli_ctx, namespace.nsg)
1566        namespace.location = nsg.location  # pylint: disable=no-member
1567        get_network_watcher_from_location(remove=True)(cmd, namespace)
1568    elif namespace.flow_log_name is not None and namespace.location is not None:
1569        get_network_watcher_from_location(remove=False)(cmd, namespace)
1570    else:
1571        raise CLIError('usage error: --nsg NSG | --location NETWORK_WATCHER_LOCATION --name FLOW_LOW_NAME')
1572
1573
1574def process_nw_topology_namespace(cmd, namespace):
1575    from msrestazure.tools import is_valid_resource_id, resource_id, parse_resource_id
1576    SubResource = cmd.get_models('SubResource')
1577    subscription_id = get_subscription_id(cmd.cli_ctx)
1578
1579    location = namespace.location
1580    rg = namespace.target_resource_group_name
1581    vnet = namespace.target_vnet
1582    subnet = namespace.target_subnet
1583
1584    vnet_id = vnet if is_valid_resource_id(vnet) else None
1585    subnet_id = subnet if is_valid_resource_id(subnet) else None
1586
1587    if rg and not vnet and not subnet:
1588        # targeting resource group - OK
1589        pass
1590    elif subnet:
1591        subnet_usage = CLIError('usage error: --subnet ID | --subnet NAME --resource-group NAME --vnet NAME')
1592        # targeting subnet - OK
1593        if subnet_id and (vnet or rg):
1594            raise subnet_usage
1595        if not subnet_id and (not rg or not vnet or vnet_id):
1596            raise subnet_usage
1597        if subnet_id:
1598            rg = parse_resource_id(subnet_id)['resource_group']
1599            namespace.target_subnet = SubResource(id=subnet)
1600        else:
1601            subnet_id = subnet_id or resource_id(
1602                subscription=subscription_id,
1603                resource_group=rg,
1604                namespace='Microsoft.Network',
1605                type='virtualNetworks',
1606                name=vnet,
1607                child_type_1='subnets',
1608                child_name_1=subnet
1609            )
1610            namespace.target_resource_group_name = None
1611            namespace.target_vnet = None
1612            namespace.target_subnet = SubResource(id=subnet_id)
1613    elif vnet:
1614        # targeting vnet - OK
1615        vnet_usage = CLIError('usage error: --vnet ID | --vnet NAME --resource-group NAME')
1616        if vnet_id and (subnet or rg):
1617            raise vnet_usage
1618        if not vnet_id and not rg or subnet:
1619            raise vnet_usage
1620        if vnet_id:
1621            rg = parse_resource_id(vnet_id)['resource_group']
1622            namespace.target_vnet = SubResource(id=vnet)
1623        else:
1624            vnet_id = vnet_id or resource_id(
1625                subscription=subscription_id,
1626                resource_group=rg,
1627                namespace='Microsoft.Network',
1628                type='virtualNetworks',
1629                name=vnet
1630            )
1631            namespace.target_resource_group_name = None
1632            namespace.target_vnet = SubResource(id=vnet_id)
1633    else:
1634        raise CLIError('usage error: --resource-group NAME | --vnet NAME_OR_ID | --subnet NAME_OR_ID')
1635
1636    # retrieve location from resource group
1637    if not location:
1638        resource_client = \
1639            get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES).resource_groups
1640        resource_group = resource_client.get(rg)
1641        namespace.location = resource_group.location  # pylint: disable=no-member
1642
1643    get_network_watcher_from_location(
1644        remove=True, watcher_name='network_watcher_name', rg_name='resource_group_name')(cmd, namespace)
1645
1646
1647def process_nw_packet_capture_create_namespace(cmd, namespace):
1648    from msrestazure.tools import is_valid_resource_id, resource_id
1649    get_network_watcher_from_vm(cmd, namespace)
1650
1651    storage_usage = CLIError('usage error: --storage-account NAME_OR_ID [--storage-path '
1652                             'PATH] [--file-path PATH] | --file-path PATH')
1653    if not namespace.storage_account and not namespace.file_path:
1654        raise storage_usage
1655
1656    if namespace.storage_path and not namespace.storage_account:
1657        raise storage_usage
1658
1659    if not is_valid_resource_id(namespace.vm):
1660        namespace.vm = resource_id(
1661            subscription=get_subscription_id(cmd.cli_ctx),
1662            resource_group=namespace.resource_group_name,
1663            namespace='Microsoft.Compute',
1664            type='virtualMachines',
1665            name=namespace.vm)
1666
1667    if namespace.storage_account and not is_valid_resource_id(namespace.storage_account):
1668        namespace.storage_account = resource_id(
1669            subscription=get_subscription_id(cmd.cli_ctx),
1670            resource_group=namespace.resource_group_name,
1671            namespace='Microsoft.Storage',
1672            type='storageAccounts',
1673            name=namespace.storage_account)
1674
1675    if namespace.file_path:
1676        file_path = namespace.file_path
1677        if not file_path.endswith('.cap'):
1678            raise CLIError("usage error: --file-path PATH must end with the '*.cap' extension")
1679        file_path = file_path.replace('/', '\\')
1680        namespace.file_path = file_path
1681
1682
1683def process_nw_troubleshooting_start_namespace(cmd, namespace):
1684    from msrestazure.tools import is_valid_resource_id, resource_id
1685    storage_usage = CLIError('usage error: --storage-account NAME_OR_ID [--storage-path PATH]')
1686    if namespace.storage_path and not namespace.storage_account:
1687        raise storage_usage
1688
1689    if not is_valid_resource_id(namespace.storage_account):
1690        namespace.storage_account = resource_id(
1691            subscription=get_subscription_id(cmd.cli_ctx),
1692            resource_group=namespace.resource_group_name,
1693            namespace='Microsoft.Storage',
1694            type='storageAccounts',
1695            name=namespace.storage_account)
1696
1697    process_nw_troubleshooting_show_namespace(cmd, namespace)
1698
1699
1700def process_nw_troubleshooting_show_namespace(cmd, namespace):
1701    from msrestazure.tools import is_valid_resource_id, resource_id
1702    resource_usage = CLIError('usage error: --resource ID | --resource NAME --resource-type TYPE '
1703                              '--resource-group NAME')
1704    id_params = [namespace.resource_type, namespace.resource_group_name]
1705    if not is_valid_resource_id(namespace.resource):
1706        if not all(id_params):
1707            raise resource_usage
1708        type_map = {
1709            'vnetGateway': 'virtualNetworkGateways',
1710            'vpnConnection': 'connections'
1711        }
1712        namespace.resource = resource_id(
1713            subscription=get_subscription_id(cmd.cli_ctx),
1714            resource_group=namespace.resource_group_name,
1715            namespace='Microsoft.Network',
1716            type=type_map[namespace.resource_type],
1717            name=namespace.resource)
1718    else:
1719        if any(id_params):
1720            raise resource_usage
1721
1722    get_network_watcher_from_resource(cmd, namespace)
1723
1724
1725def process_nw_config_diagnostic_namespace(cmd, namespace):
1726    from msrestazure.tools import is_valid_resource_id, resource_id
1727
1728    # validate target resource
1729    resource_usage = CLIError('usage error: --resource ID | --resource NAME --resource-type TYPE '
1730                              '--resource-group NAME [--parent PATH]')
1731
1732    # omit --parent since it is optional
1733    id_params = [namespace.resource_type, namespace.resource_group_name]
1734    if not is_valid_resource_id(namespace.resource):
1735        if not all(id_params):
1736            raise resource_usage
1737        # infer resource namespace
1738        NAMESPACES = {
1739            'virtualMachines': 'Microsoft.Compute',
1740            'applicationGateways': 'Microsoft.Network',
1741            'networkInterfaces': 'Microsoft.Network'
1742        }
1743        resource_namespace = NAMESPACES[namespace.resource_type]
1744        if namespace.parent:
1745            # special case for virtualMachineScaleSets/NetworkInterfaces, since it is
1746            # the only one to need `--parent`.
1747            resource_namespace = 'Microsoft.Compute'
1748        namespace.resource = resource_id(
1749            subscription=get_subscription_id(cmd.cli_ctx),
1750            resource_group=namespace.resource_group_name,
1751            namespace=resource_namespace,
1752            type=namespace.resource_type,
1753            parent=namespace.parent,
1754            name=namespace.resource)
1755    elif any(id_params) or namespace.parent:
1756        raise resource_usage
1757
1758    # validate query
1759    query_usage = CLIError('usage error: --queries JSON | --destination DEST --source SRC --direction DIR '
1760                           '--port PORT --protocol PROTOCOL')
1761    query_params = [namespace.destination, namespace.source, namespace.direction, namespace.protocol,
1762                    namespace.destination_port]
1763    if namespace.queries:
1764        if any(query_params):
1765            raise query_usage
1766    elif not all(query_params):
1767        raise query_usage
1768
1769    get_network_watcher_from_resource(cmd, namespace)
1770
1771
1772def process_lb_outbound_rule_namespace(cmd, namespace):
1773    from msrestazure.tools import is_valid_resource_id
1774
1775    validate_frontend_ip_configs(cmd, namespace)
1776
1777    if namespace.backend_address_pool:
1778        if not is_valid_resource_id(namespace.backend_address_pool):
1779            namespace.backend_address_pool = _generate_lb_subproperty_id(
1780                cmd.cli_ctx, namespace, 'backendAddressPools', namespace.backend_address_pool)
1781
1782
1783def process_list_delegations_namespace(cmd, namespace):
1784    if not namespace.resource_group_name and not namespace.location:
1785        raise CLIError('usage error: --location LOCATION | --resource-group NAME [--location LOCATION]')
1786
1787    if not namespace.location:
1788        get_default_location_from_resource_group(cmd, namespace)
1789
1790
1791def validate_ag_address_pools(cmd, namespace):
1792    from msrestazure.tools import is_valid_resource_id, resource_id
1793    address_pools = namespace.app_gateway_backend_address_pools
1794    gateway_name = namespace.application_gateway_name
1795    delattr(namespace, 'application_gateway_name')
1796    if not address_pools:
1797        return
1798    ids = []
1799    for item in address_pools:
1800        if not is_valid_resource_id(item):
1801            if not gateway_name:
1802                raise CLIError('usage error: --app-gateway-backend-pools IDS | --gateway-name NAME '
1803                               '--app-gateway-backend-pools NAMES')
1804            item = resource_id(
1805                subscription=get_subscription_id(cmd.cli_ctx),
1806                resource_group=namespace.resource_group_name,
1807                namespace='Microsoft.Network',
1808                type='applicationGateways',
1809                name=gateway_name,
1810                child_type_1='backendAddressPools',
1811                child_name_1=item)
1812            ids.append(item)
1813    namespace.app_gateway_backend_address_pools = ids
1814
1815
1816def validate_custom_error_pages(namespace):
1817    if not namespace.custom_error_pages:
1818        return
1819
1820    values = []
1821    for item in namespace.custom_error_pages:
1822        try:
1823            (code, url) = item.split('=')
1824            values.append({'statusCode': code, 'customErrorPageUrl': url})
1825        except (ValueError, TypeError):
1826            raise CLIError('usage error: --custom-error-pages STATUS_CODE=URL [STATUS_CODE=URL ...]')
1827    namespace.custom_error_pages = values
1828
1829
1830def validate_custom_headers(namespace):
1831    if not namespace.monitor_custom_headers:
1832        return
1833
1834    values = []
1835    for item in namespace.monitor_custom_headers:
1836        try:
1837            item_split = item.split('=', 1)
1838            values.append({'name': item_split[0], 'value': item_split[1]})
1839        except IndexError:
1840            raise CLIError('usage error: --custom-headers KEY=VALUE')
1841
1842    namespace.monitor_custom_headers = values
1843
1844
1845def validate_status_code_ranges(namespace):
1846    if not namespace.status_code_ranges:
1847        return
1848
1849    values = []
1850    for item in namespace.status_code_ranges:
1851        item_split = item.split('-', 1)
1852        usage_error = CLIError('usage error: --status-code-ranges VAL | --status-code-ranges MIN-MAX')
1853        try:
1854            if len(item_split) == 1:
1855                values.append({'min': int(item_split[0]), 'max': int(item_split[0])})
1856            elif len(item_split) == 2:
1857                values.append({'min': int(item_split[0]), 'max': int(item_split[1])})
1858            else:
1859                raise usage_error
1860        except ValueError:
1861            raise usage_error
1862
1863    namespace.status_code_ranges = values
1864
1865
1866def validate_subnet_ranges(namespace):
1867    if not namespace.subnets:
1868        return
1869
1870    values = []
1871    for item in namespace.subnets:
1872        try:
1873            item_split = item.split('-', 1)
1874            if len(item_split) == 2:
1875                values.append({'first': item_split[0], 'last': item_split[1]})
1876                continue
1877        except ValueError:
1878            pass
1879
1880        try:
1881            item_split = item.split(':', 1)
1882            if len(item_split) == 2:
1883                values.append({'first': item_split[0], 'scope': item_split[1]})
1884                continue
1885        except ValueError:
1886            pass
1887
1888        values.append({'first': item})
1889
1890    namespace.subnets = values
1891
1892
1893# pylint: disable=too-few-public-methods
1894class WafConfigExclusionAction(argparse.Action):
1895    def __call__(self, parser, namespace, values, option_string=None):
1896        cmd = namespace._cmd  # pylint: disable=protected-access
1897        ApplicationGatewayFirewallExclusion = cmd.get_models('ApplicationGatewayFirewallExclusion')
1898        if not namespace.exclusions:
1899            namespace.exclusions = []
1900        if isinstance(values, list):
1901            values = ' '.join(values)
1902        try:
1903            variable, op, selector = values.split(' ')
1904        except (ValueError, TypeError):
1905            raise CLIError('usage error: --exclusion VARIABLE OPERATOR VALUE')
1906        namespace.exclusions.append(ApplicationGatewayFirewallExclusion(
1907            match_variable=variable,
1908            selector_match_operator=op,
1909            selector=selector
1910        ))
1911
1912
1913def get_header_configuration_validator(dest):
1914    def validator(namespace):
1915        values = getattr(namespace, dest, None)
1916        if not values:
1917            return
1918
1919        results = []
1920        for item in values:
1921            key, value = item.split('=', 1)
1922            results.append({
1923                'header_name': key,
1924                'header_value': value
1925            })
1926        setattr(namespace, dest, results)
1927
1928    return validator
1929
1930
1931def process_private_link_resource_id_argument(cmd, namespace):
1932    if all([namespace.resource_group_name,
1933            namespace.name,
1934            namespace.resource_provider]):
1935        logger.warning("Resource ID will be ignored since other three arguments have been provided.")
1936        del namespace.id
1937        return
1938
1939    if not (namespace.id or all([namespace.resource_group_name,
1940                                 namespace.name,
1941                                 namespace.resource_provider])):
1942        raise CLIError("usage error: --id / -g -n --type")
1943
1944    from msrestazure.tools import is_valid_resource_id, parse_resource_id
1945    if not is_valid_resource_id(namespace.id):
1946        raise CLIError("Resource ID is not invalid. Please check it.")
1947    split_resource_id = parse_resource_id(namespace.id)
1948    cmd.cli_ctx.data['subscription_id'] = split_resource_id['subscription']
1949    namespace.resource_group_name = split_resource_id['resource_group']
1950    namespace.name = split_resource_id['name']
1951    namespace.resource_provider = '{}/{}'.format(split_resource_id['namespace'], split_resource_id['type'])
1952    del namespace.id
1953
1954
1955def process_private_endpoint_connection_id_argument(cmd, namespace):
1956    from azure.cli.core.util import parse_proxy_resource_id
1957    if all([namespace.resource_group_name,
1958            namespace.name,
1959            namespace.resource_provider,
1960            namespace.resource_name]):
1961        logger.warning("Resource ID will be ignored since other three arguments have been provided.")
1962        del namespace.connection_id
1963        return
1964
1965    if not (namespace.connection_id or all([namespace.resource_group_name,
1966                                            namespace.name,
1967                                            namespace.resource_provider,
1968                                            namespace.resource_name])):
1969        raise CLIError("usage error: --id / -g -n --type --resource-name")
1970
1971    result = parse_proxy_resource_id(namespace.connection_id)
1972    cmd.cli_ctx.data['subscription_id'] = result['subscription']
1973    namespace.resource_group_name = result['resource_group']
1974    namespace.resource_name = result['name']
1975    namespace.resource_provider = '{}/{}'.format(result['namespace'], result['type'])
1976    namespace.name = result['child_name_1']
1977    del namespace.connection_id
1978
1979
1980def process_vnet_name_or_id(cmd, namespace):
1981    from azure.mgmt.core.tools import is_valid_resource_id, resource_id
1982    if namespace.vnet and not is_valid_resource_id(namespace.vnet):
1983        namespace.vnet = resource_id(
1984            subscription=get_subscription_id(cmd.cli_ctx),
1985            resource_group=namespace.resource_group_name,
1986            namespace='Microsoft.Network',
1987            type='virtualNetworks',
1988            name=namespace.vnet)
1989
1990
1991def process_appgw_waf_policy_update(cmd, namespace):    # pylint: disable=unused-argument
1992    rule_group_name = namespace.rule_group_name
1993    rules = namespace.rules
1994
1995    if rules is None and rule_group_name is not None:
1996        raise CLIError('--rules and --rule-group-name must be provided at the same time')
1997    if rules is not None and rule_group_name is None:
1998        raise CLIError('--rules and --rule-group-name must be provided at the same time')
1999