Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/command_modules/azure-cli-network/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Release History

2.2.11
++++++
* `traffic-manager profile create/update`: Add support for `--custom-headers` and `--status-code-ranges`. Add support for new routing types: Subnet and Multivalue.
* `traffic-manager endpoint create/update`: Add support for `--custom-headers` and `--subnets`.
* `ddos-protection update`: Fix issue where supplying `--vnets ""` to remove vnets caused a strack trace.
* `watcher flow-log configure`: Add support for `--format` and `--log-version`.
* `dns zone update`: Finished issue where using "" to clear resolution and registration VNets didn't work.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_asg_validator, get_vnet_validator, validate_ip_tags, validate_ddos_name_or_id,
validate_service_endpoint_policy, validate_delegations, validate_subresource_list,
validate_er_peer_circuit, validate_ag_address_pools, validate_custom_error_pages,
validate_custom_headers, validate_status_code_ranges, validate_subnet_ranges,
WafConfigExclusionAction)
from azure.mgmt.trafficmanager.models import MonitorProtocol, ProfileStatus
from azure.cli.command_modules.network._completers import (
Expand Down Expand Up @@ -914,7 +915,7 @@ def load_arguments(self, _):
c.argument('traffic_manager_profile_name', name_arg_type, id_part='name', help='Traffic manager profile name', completer=get_resource_name_completion_list('Microsoft.Network/trafficManagerProfiles'))
c.argument('profile_name', name_arg_type, id_part='name', completer=get_resource_name_completion_list('Microsoft.Network/trafficManagerProfiles'))
c.argument('profile_status', options_list=['--status'], help='Status of the Traffic Manager profile.', arg_type=get_enum_type(ProfileStatus))
c.argument('routing_method', help='Routing method.', arg_type=get_enum_type(['Performance', 'Weighted', 'Priority', 'Geographic']))
c.argument('routing_method', help='Routing method.', arg_type=get_enum_type(['Performance', 'Weighted', 'Priority', 'Geographic', 'Multivalue', 'Subnet']))
c.argument('unique_dns_name', help="Relative DNS name for the traffic manager profile. Resulting FQDN will be `<unique-dns-name>.trafficmanager.net` and must be globally unique.")
c.argument('ttl', help='DNS config time-to-live in seconds.', type=int)

Expand All @@ -925,6 +926,8 @@ def load_arguments(self, _):
c.argument('timeout', help='The time in seconds allowed for endpoints to response to a health check.', type=int)
c.argument('interval', help='The interval in seconds at which health checks are conducted.', type=int)
c.argument('max_failures', help='The number of consecutive failed health checks tolerated before an endpoint is considered degraded.', type=int)
c.argument('monitor_custom_headers', options_list='--custom-headers', help='Space-separated list of NAME=VALUE pairs.', nargs='+', validator=validate_custom_headers)
c.argument('status_code_ranges', help='Space-separated list of status codes in MIN-MAX or VAL format.', nargs='+', validator=validate_status_code_ranges)

with self.argument_context('network traffic-manager profile update') as c:
c.argument('monitor_protocol', monitor_protocol_type, default=None)
Expand All @@ -947,6 +950,8 @@ def load_arguments(self, _):
c.argument('target_resource_id', help="The Azure Resource URI of the endpoint. Not applicable for endpoints of type 'ExternalEndpoints'.")
c.argument('weight', help="Weight of the endpoint when using the 'Weighted' traffic routing method. Values range from 1 to 1000.", type=int)
c.argument('geo_mapping', help="Space-separated list of country/region codes mapped to this endpoint when using the 'Geographic' routing method.", nargs='+')
c.argument('subnets', nargs='+', help='Space-separated list of subnet CIDR prefixes (10.0.0.0/24) or subnet ranges (10.0.0.0-11.0.0.0).', validator=validate_subnet_ranges)
c.argument('monitor_custom_headers', nargs='+', options_list='--custom-headers', help='Space-separated list of custom headers in KEY=VALUE format.', validator=validate_custom_headers)

with self.argument_context('network traffic-manager endpoint create') as c:
c.argument('target', help='Fully-qualified DNS name of the endpoint.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,9 @@ def process_tm_endpoint_create_namespace(cmd, namespace):
'endpoint_location': '--endpoint-location',
'geo_mapping': '--geo-mapping'
}
validate_subnet_ranges(namespace)
validate_custom_headers(namespace)

required_options = []

# determine which options are required based on profile and routing method
Expand Down Expand Up @@ -1280,6 +1283,72 @@ def validate_custom_error_pages(namespace):
namespace.custom_error_pages = values


def validate_custom_headers(namespace):

if not namespace.monitor_custom_headers:
return

values = []
for item in namespace.monitor_custom_headers:
try:
item_split = item.split('=', 1)
values.append({'name': item_split[0], 'value': item_split[1]})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If values are allowed to have escaped equals signs, then we'll need a more robust split strategy here. If it's always a base64 encoded or similar, then we're okay.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same strategy used that has always been used with --tags and we've not had complaints. I believe it is because of the simpler constraints on the key. If there's an equal sign in the value (escaped or not) it shouldn't be an issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err, so I may not need to worry about this. If the headers have already been correctly URL encoded, any equal sign should just show up as %3D. I imagine we're okay requiring properly encoded info here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(sorry, looks like there was a race condition, and I didn't see your message when I was typing mine.)

except IndexError:
raise CLIError('usage error: --custom-headers KEY=VALUE')

namespace.monitor_custom_headers = values


def validate_status_code_ranges(namespace):

if not namespace.status_code_ranges:
return

values = []
for item in namespace.status_code_ranges:
item_split = item.split('-', 1)
usage_error = CLIError('usage error: --status-code-ranges VAL | --status-code-ranges MIN-MAX')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are MIN and MAX inclusive or exclusive? As written, I assume inclusive.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SDK is ambiguous on this, but yes I would assume inclusive. If you give a single value, it simply uses that value as both min and max.

try:
if len(item_split) == 1:
values.append({'min': int(item_split[0]), 'max': int(item_split[0])})
elif len(item_split) == 2:
values.append({'min': int(item_split[0]), 'max': int(item_split[1])})
else:
raise usage_error
except ValueError:
raise usage_error

namespace.status_code_ranges = values


def validate_subnet_ranges(namespace):

if not namespace.subnets:
return

values = []
for item in namespace.subnets:
try:
item_split = item.split('-', 1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike above, I think subnet strings are constrained enough that I don't think it's a problem to just use a split.

if len(item_split) == 2:
values.append({'first': item_split[0], 'last': item_split[1]})
continue
except ValueError:
pass

try:
item_split = item.split(':', 1)
if len(item_split) == 2:
values.append({'first': item_split[0], 'scope': item_split[1]})
continue
except ValueError:
pass

values.append({'first': item})

namespace.subnets = values


# pylint: disable=too-few-public-methods
class WafConfigExclusionAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3010,7 +3010,8 @@ def create_traffic_manager_profile(cmd, traffic_manager_profile_name, resource_g
routing_method, unique_dns_name, monitor_path=None,
monitor_port=80, monitor_protocol=MonitorProtocol.http.value,
profile_status=ProfileStatus.enabled.value,
ttl=30, tags=None, interval=None, timeout=None, max_failures=None):
ttl=30, tags=None, interval=None, timeout=None, max_failures=None,
monitor_custom_headers=None, status_code_ranges=None):
from azure.mgmt.trafficmanager import TrafficManagerManagementClient
from azure.mgmt.trafficmanager.models import Profile, DnsConfig, MonitorConfig
client = get_mgmt_service_client(cmd.cli_ctx, TrafficManagerManagementClient).profiles
Expand All @@ -3024,13 +3025,16 @@ def create_traffic_manager_profile(cmd, traffic_manager_profile_name, resource_g
path=monitor_path,
interval_in_seconds=interval,
timeout_in_seconds=timeout,
tolerated_number_of_failures=max_failures))
tolerated_number_of_failures=max_failures,
custom_headers=monitor_custom_headers,
expected_status_code_ranges=status_code_ranges))
return client.create_or_update(resource_group_name, traffic_manager_profile_name, profile)


def update_traffic_manager_profile(instance, profile_status=None, routing_method=None, tags=None,
monitor_protocol=None, monitor_port=None, monitor_path=None,
ttl=None, timeout=None, interval=None, max_failures=None):
ttl=None, timeout=None, interval=None, max_failures=None,
monitor_custom_headers=None, status_code_ranges=None):
if tags is not None:
instance.tags = tags
if profile_status is not None:
Expand All @@ -3054,6 +3058,10 @@ def update_traffic_manager_profile(instance, profile_status=None, routing_method
instance.monitor_config.timeout_in_seconds = timeout
if max_failures is not None:
instance.monitor_config.tolerated_number_of_failures = max_failures
if monitor_custom_headers is not None:
instance.monitor_config.custom_headers = monitor_custom_headers
if status_code_ranges is not None:
instance.monitor_config.expected_status_code_ranges = status_code_ranges

# TODO: Remove workaround after https://github.com/Azure/azure-rest-api-specs/issues/1940 fixed
for endpoint in instance.endpoints:
Expand All @@ -3068,7 +3076,8 @@ def create_traffic_manager_endpoint(cmd, resource_group_name, profile_name, endp
target_resource_id=None, target=None,
endpoint_status=None, weight=None, priority=None,
endpoint_location=None, endpoint_monitor_status=None,
min_child_endpoints=None, geo_mapping=None):
min_child_endpoints=None, geo_mapping=None,
monitor_custom_headers=None, subnets=None):
from azure.mgmt.trafficmanager import TrafficManagerManagementClient
from azure.mgmt.trafficmanager.models import Endpoint
ncf = get_mgmt_service_client(cmd.cli_ctx, TrafficManagerManagementClient).endpoints
Expand All @@ -3078,7 +3087,9 @@ def create_traffic_manager_endpoint(cmd, resource_group_name, profile_name, endp
endpoint_location=endpoint_location,
endpoint_monitor_status=endpoint_monitor_status,
min_child_endpoints=min_child_endpoints,
geo_mapping=geo_mapping)
geo_mapping=geo_mapping,
subnets=subnets,
custom_headers=monitor_custom_headers)

return ncf.create_or_update(resource_group_name, profile_name, endpoint_type, endpoint_name,
endpoint)
Expand All @@ -3087,7 +3098,8 @@ def create_traffic_manager_endpoint(cmd, resource_group_name, profile_name, endp
def update_traffic_manager_endpoint(instance, endpoint_type=None, endpoint_location=None,
endpoint_status=None, endpoint_monitor_status=None,
priority=None, target=None, target_resource_id=None,
weight=None, min_child_endpoints=None, geo_mapping=None):
weight=None, min_child_endpoints=None, geo_mapping=None,
subnets=None, monitor_custom_headers=None):
if endpoint_location is not None:
instance.endpoint_location = endpoint_location
if endpoint_status is not None:
Expand All @@ -3106,6 +3118,10 @@ def update_traffic_manager_endpoint(instance, endpoint_type=None, endpoint_locat
instance.min_child_endpoints = min_child_endpoints
if geo_mapping is not None:
instance.geo_mapping = geo_mapping
if subnets is not None:
instance.subnets = subnets
if monitor_custom_headers:
instance.custom_headers = monitor_custom_headers

return instance

Expand Down
Loading