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=line-too-long
7import argparse
8
9from azure.cli.command_modules.servicefabric._validators import (
10    validate_create_application, validate_create_managed_application,
11    validate_create_managed_cluster, validate_create_managed_service,
12    validate_create_service, validate_update_application,
13    validate_update_managed_application, validate_update_managed_service,
14    validate_create_managed_service_correlation, validate_create_managed_service_load_metric,
15    validate_update_managed_service_load_metric, validate_update_managed_service_correlation)
16from azure.cli.core.commands.parameters import (get_enum_type,
17                                                get_three_state_flag,
18                                                resource_group_name_type,
19                                                tags_type)
20from azure.cli.core.commands.validators import get_default_location_from_resource_group
21from azure.cli.core.util import CLIError, get_json_object
22from azure.mgmt.servicefabricmanagedclusters.models import (FailureAction,
23                                                            MoveCost,
24                                                            PartitionScheme,
25                                                            RollingUpgradeMode,
26                                                            ServiceKind,
27                                                            DiskType,
28                                                            ClusterUpgradeMode,
29                                                            ClusterUpgradeCadence)
30from knack.arguments import CLIArgumentType
31
32
33def load_arguments(self, _):  # pylint: disable=too-many-statements
34    # PARAMETER REGISTRATION
35    application_parameters = CLIArgumentType(
36        options_list=['--parameters', '--application-parameters'],
37        action=AddAppParamsAction,
38        nargs='+',
39        help='Specify the application parameters as key/value pairs. These parameters must exist in the application manifest. '
40        'for example: --application-parameters param1=value1 param2=value2')
41
42    minimum_nodes = CLIArgumentType(
43        options_list=['--min-nodes', '--minimum-nodes'],
44        help='Specify the minimum number of nodes where Service Fabric will reserve capacity for this application, '
45        'this does not mean that the application is guaranteed to have replicas on all those nodes. The value of this parameter must be a non-negative integer. '
46        'Default value for this is zero, which means no capacity is reserved for the application.')
47
48    maximum_nodes = CLIArgumentType(
49        options_list=['--max-nodes', '--maximum-nodes'],
50        help='Specify the maximum number of nodes on which to place an application. '
51        'The value of this parameter must be a non-negative integer. The default value is 0, which indicates the application can be placed on any number of nodes in the cluster.')
52
53    application_type_version = CLIArgumentType(
54        options_list=['--version', '--application-type-version'],
55        help='Specify the application type version.')
56
57    package_url = CLIArgumentType(
58        help='Specify the url of the application package sfpkg file.')
59
60    with self.argument_context('sf') as c:
61        c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None, help='Specify the resource group name. You can configure the default group using `az configure --defaults group=<name>`')
62        c.argument('cluster_name', options_list=['--cluster-name', '-c'], help='Specify the name of the cluster, if not given it will be same as resource group name')
63        c.argument('location', validator=get_default_location_from_resource_group)
64        c.argument('secret_identifier', help='The existing Azure key vault secret URL')
65        c.argument('certificate_file', help='The existing certificate file path for the primary cluster certificate.')
66        c.argument('parameter_file', help='The path to the template parameter file.')
67        c.argument('template_file', help='The path to the template file.')
68        c.argument('vm_password', help='The password of the Vm')
69        c.argument('certificate_output_folder', options_list=['--certificate-output-folder', '--cert-out-folder'], help='The folder of the new certificate file to be created.')
70        c.argument('certificate_password', help='The password of the certificate file.')
71        c.argument('certificate_subject_name', options_list=['--certificate-subject-name', '--cert-subject-name'], help='The subject name of the certificate to be created.')
72        c.argument('vault_resource_group_name', options_list=['--vault-rg', c.deprecate(target='--vault-resource-group', redirect='--vault-rg', hide=True)],
73                   help='Key vault resource group name, if not given it will be cluster resource group name')
74        c.argument('vault_name', help='Azure key vault name, it not given it will be the cluster resource group name')
75        c.argument('cluster_size', options_list=['--cluster-size', '-s'], help='The number of nodes in the cluster. Default are 5 nodes')
76        c.argument('vm_sku', help='VM Sku')
77        c.argument('vm_user_name', help='The user name for logging to Vm. Default will be adminuser')
78        c.argument('vm_os', arg_type=get_enum_type(['WindowsServer2012R2Datacenter',
79                                                    'WindowsServer2016Datacenter',
80                                                    'WindowsServer2016DatacenterwithContainers',
81                                                    'UbuntuServer1604',
82                                                    'WindowsServer1709',
83                                                    'WindowsServer1709withContainers',
84                                                    'WindowsServer1803withContainers',
85                                                    'WindowsServer1809withContainers',
86                                                    'WindowsServer2019Datacenter',
87                                                    'WindowsServer2019DatacenterwithContainers']),
88                   default='WindowsServer2016Datacenter', options_list=['--vm-os', '--os'],
89                   help='The Operating System of the VMs that make up the cluster.')
90        c.argument('node_type', help='the Node type name.')
91
92    # cluster
93    with self.argument_context('sf cluster list') as c:
94        c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None, help='The resource group name')
95
96    with self.argument_context('sf client certificate') as c:
97        c.argument('certificate_common_name', help='client certificate common name.')
98        c.argument('admin_client_thumbprints', options_list=['--admin-client-thumbprints', '--admin-client-tps'], nargs='+', help='Space-separated list of client certificate thumbprint that only has admin permission, ')
99        c.argument('certificate_issuer_thumbprint', options_list=['--certificate-issuer-thumbprint', '--cert-issuer-tp'], help='client certificate issuer thumbprint.')
100
101    with self.argument_context('sf cluster client-certificate') as c:
102        c.argument('is_admin', help='Client authentication type.')
103        c.argument('certificate_issuer_thumbprint', options_list=['--certificate-issuer-thumbprint', '--cert-issuer-tp'], help='client certificate issuer thumbprint.')
104        c.argument('certificate_common_name', options_list=['--certificate-common-name', '--cert-common-name'], help='client certificate common name.')
105        c.argument('admin_client_thumbprints', options_list=['--admin-client-thumbprints', '--admin-client-tps'], nargs='+', help='client certificate thumbprint that only has admin permission.')
106        c.argument('readonly_client_thumbprints', options_list=['--readonly-client-thumbprints', '--readonly-client-tps'], nargs='+', help='Space-separated list of client certificate thumbprint that has read only permission.')
107
108    with self.argument_context('sf cluster client-certificate add') as c:
109        c.argument('thumbprint', help='client certificate thumbprint.')
110
111    with self.argument_context('sf cluster client-certificate remove') as c:
112        c.argument('thumbprints', nargs='+', help='A single or Space-separated list of client certificate thumbprint(s) to be remove.')
113
114    with self.argument_context('sf cluster node') as c:
115        c.argument('number_of_nodes_to_add', options_list=['--number-of-nodes-to-add', '--nodes-to-add'], help='number of nodes to add.')
116        c.argument('number_of_nodes_to_remove', options_list=['--number-of-nodes-to-remove', '--nodes-to-remove'], help='number of nodes to remove.')
117
118    with self.argument_context('sf cluster node-type') as c:
119        c.argument('capacity', help='The capacity tag applied to nodes in the node type. The cluster resource manager uses these tags to understand how much capacity a node has.')
120        c.argument('vm_tier', help='VM tier.')
121
122    with self.argument_context('sf cluster') as c:
123        c.argument('durability_level', arg_type=get_enum_type(['Bronze', 'Silver', 'Gold']), help='durability level.')
124
125    with self.argument_context('sf cluster setting') as c:
126        c.argument('parameter', help='parameter name')
127        c.argument('section', help='section name')
128        c.argument('value', help='Specify the value')
129        c.argument('settings_section_description', options_list=['--settings-section-description', '--settings-section'], help='Specify the value')
130
131    with self.argument_context('sf cluster upgrade-type set') as c:
132        c.argument('version', help='cluster code version')
133        c.argument('upgrade_mode', arg_type=get_enum_type(['manual', 'automatic']), help='cluster upgrade mode')
134
135    with self.argument_context('sf cluster reliability') as c:
136        c.argument('reliability_level', arg_type=get_enum_type(['Bronze', 'Silver', 'Gold', 'Platinum']), help='durability level.')
137        c.argument('auto_add_node', help='Add node count automatically when changing reliability.')
138
139    with self.argument_context('sf cluster setting set') as c:
140        c.argument('settings_section_description', options_list=['--settings-section-description', '--settings-section'], type=get_json_object,
141                   help='JSON encoded parameters configuration. Use @{file} to load from a file. '
142                        'For example: [{"section": "NamingService","parameter": "MaxOperationTimeout","value": 1000},{"section": "MaxFileOperationTimeout","parameter": "Max2","value": 1000]')
143
144    with self.argument_context('sf cluster setting remove') as c:
145        c.argument('settings_section_description', options_list=['--settings-section-description', '--settings-section'], type=get_json_object,
146                   help='JSON encoded parameters configuration. Use @{file} to load from a file. '
147                        'For example: [{"section": "NamingService","parameter": "MaxOperationTimeout"}]')
148
149    with self.argument_context('sf cluster client-certificate remove') as c:
150        c.argument('client_certificate_common_names', options_list=['--client-certificate-common-names', '--client-cert-cn'], type=get_json_object,
151                   help='JSON encoded parameters configuration. Use @{file} to load from a file. '
152                        'For example: [{"certificateCommonName": "test.com","certificateIssuerThumbprint": "22B4AE296B504E512DF880A77A2CAE20200FF922"}]')
153
154    with self.argument_context('sf cluster client-certificate add') as c:
155        c.argument('client_certificate_common_names', options_list=['--client-certificate-common-names', '--client-cert-cn'], type=get_json_object,
156                   help='JSON encoded parameters configuration. Use @{file} to load from a file. '
157                        'For example: [{"isAdmin":true, "certificateCommonName": "test.com", '
158                        '"certificateIssuerThumbprint": "22B4AE296B504E512DF880A77A2CAE20200FF922"}]')
159
160    # application-type
161    with self.argument_context('sf application-type') as c:
162        c.argument('application_type_name', options_list=['--name', '--application-type-name'], help='Specify the application type name.')
163
164    # application-type version
165    with self.argument_context('sf application-type version') as c:
166        c.argument('version', arg_type=application_type_version)
167        c.argument('package_url', arg_type=package_url)
168
169    # application
170    with self.argument_context('sf application') as c:
171        c.argument('application_name', options_list=['--name', '--application-name'], help='Specify the application name.')
172
173    with self.argument_context('sf application update', validator=validate_update_application) as c:
174        c.argument('application_type_version', arg_type=application_type_version)
175        c.argument('application_parameters', arg_type=application_parameters)
176        c.argument('minimum_nodes', arg_type=minimum_nodes)
177        c.argument('maximum_nodes', arg_type=maximum_nodes)
178        c.argument('force_restart', arg_type=get_three_state_flag(),
179                   help='Indicates that the service host restarts even if the upgrade is a configuration-only change.')
180        c.argument('service_type_health_policy_map', options_list=['--service-type-health-policy-map', '--service-type-policy'],
181                   help='Specify the map of the health policy to use for different service types as a hash table in the following format: {\"ServiceTypeName\" : \"MaxPercentUnhealthyPartitionsPerService,MaxPercentUnhealthyReplicasPerPartition,MaxPercentUnhealthyServices\"}. For example: @{ \"ServiceTypeName01\" = \"5,10,5\"; \"ServiceTypeName02\" = \"5,5,5\" }')
182
183    with self.argument_context('sf application update', arg_group='Upgrade description') as c:
184        c.argument('upgrade_replica_set_check_timeout', options_list=['--upgrade-replica-set-check-timeout', '--replica-check-timeout', '--rep-check-timeout'],
185                   help='Specify the maximum time, in seconds, that Service Fabric waits for a service to reconfigure into a safe state, if not already in a safe state, before Service Fabric proceeds with the upgrade.')
186        c.argument('failure_action', arg_type=get_enum_type(['Rollback', 'Manual']),
187                   help='Specify the action to take if the monitored upgrade fails. The acceptable values for this parameter are Rollback or Manual.')
188        c.argument('health_check_retry_timeout', options_list=['--hc-retry-timeout', '--health-check-retry-timeout'],
189                   help='Specify the duration, in seconds, after which Service Fabric retries the health check if the previous health check fails.')
190        c.argument('health_check_wait_duration', options_list=['--hc-wait-duration', '--health-check-wait-duration'],
191                   help='Specify the duration, in seconds, that Service Fabric waits before it performs the initial health check after it finishes the upgrade on the upgrade domain.')
192        c.argument('health_check_stable_duration', options_list=['--hc-stable-duration', '--health-check-stable-duration'],
193                   help='Specify the duration, in seconds, that Service Fabric waits in order to verify that the application is stable before moving to the next upgrade domain or completing the upgrade. This wait duration prevents undetected changes of health right after the health check is performed.')
194        c.argument('upgrade_domain_timeout', options_list=['--ud-timeout', '--upgrade-domain-timeout'],
195                   help='Specify the maximum time, in seconds, that Service Fabric takes to upgrade a single upgrade domain. After this period, the upgrade fails.')
196        c.argument('upgrade_timeout',
197                   help='Specify the maximum time, in seconds, that Service Fabric takes for the entire upgrade. After this period, the upgrade fails.')
198        c.argument('consider_warning_as_error', options_list=['--warning-as-error', '--consider-warning-as-error'], arg_type=get_three_state_flag(),
199                   help='Indicates whether to treat a warning health event as an error event during health evaluation.')
200        c.argument('default_service_type_max_percent_unhealthy_partitions_per_service', options_list=['--max-porcent-unhealthy-partitions', '--max-unhealthy-parts'],
201                   help='Specify the maximum percent of unhelthy partitions per service allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are form 0 to 100.')
202        c.argument('default_service_type_max_percent_unhealthy_replicas_per_partition', options_list=['--max-porcent-unhealthy-replicas', '--max-unhealthy-reps'],
203                   help='Specify the maximum percent of unhelthy replicas per service allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are form 0 to 100.')
204        c.argument('default_service_type_max_percent_unhealthy_services', options_list=['--max-porcent-unhealthy-services', '--max-unhealthy-servs'],
205                   help='Specify the maximum percent of unhelthy services allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are form 0 to 100.')
206        c.argument('max_percent_unhealthy_deployed_applications', options_list=['--max-porcent-unhealthy-apps', '--max-unhealthy-apps'],
207                   help='Specify the maximum percentage of the application instances deployed on the nodes in the cluster that have a health state of error before the application health state for the cluster is error. Allowed values are form 0 to 100.')
208
209    with self.argument_context('sf application create', validator=validate_create_application) as c:
210        c.argument('application_type_name', options_list=['--type-name', '--application-type-name'], help='Specify the application type name.')
211        c.argument('application_type_version', arg_type=application_type_version)
212        c.argument('package_url', arg_type=package_url)
213        c.argument('application_parameters', arg_type=application_parameters)
214        c.argument('minimum_nodes', arg_type=minimum_nodes)
215        c.argument('maximum_nodes', arg_type=maximum_nodes)
216
217    # service
218    with self.argument_context('sf service') as c:
219        c.argument('service_name', options_list=['--name', '--service-name'],
220                   help='Specify the name of the service. The application name must be a prefix of the service name, for example: appName~serviceName')
221
222    with self.argument_context('sf service create', validator=validate_create_service) as c:
223        c.argument('service_type',
224                   help='Specify the service type name of the application, it should exist in the application manifest.')
225        c.argument('application_name', options_list=['--application', '--application-name'],
226                   help='Specify the name of the service. The application name must be a prefix of the service name, for example: appName~serviceName')
227        c.argument('state', arg_type=get_enum_type(['stateless', 'stateful']), help='Specify if the service is stateless or stateful.')
228        c.argument('instance_count', help='Specify the instance count for the stateless service. If -1 is used, it means it will run on all the nodes.')
229        c.argument('min_replica_set_size', options_list=['--min-replica-set-size', '--min-replica'], help='Specify the min replica set size for the stateful service.')
230        c.argument('target_replica_set_size', options_list=['--target-replica-set-size', '--target-replica'], help='Specify the target replica set size for the stateful service.')
231        c.argument('default_move_cost', arg_type=get_enum_type(['Zero', 'Low', 'Medium', 'High']),
232                   help='Specify the default cost for a move. Higher costs make it less likely that the Cluster Resource Manager will move the replica when trying to balance the cluster.')
233        c.argument('partition_scheme', arg_type=get_enum_type(['singleton', 'uniformInt64', 'named']),
234                   help='Specify what partition scheme to use. '
235                   'Singleton partitions are typically used when the service does not require any additional routing. '
236                   'UniformInt64 means that each partition owns a range of int64 keys. '
237                   'Named is usually for services with data that can be bucketed, within a bounded set. Some common examples of data fields used as named partition keys would be regions, postal codes, customer groups, or other business boundaries.')
238
239    # managed cluster
240
241    with self.argument_context('sf managed-cluster create', validator=validate_create_managed_cluster) as c:
242        c.argument('admin_password', help='Admin password used for the virtual machines.')
243        c.argument('admin_user_name', help='Admin user used for the virtual machines.', default='vmadmin')
244        c.argument('dns_name', help='Cluster\'s dns name.')
245        c.argument('sku', help='Cluster\'s Sku, the options are Basic: it will have a minimum of 3 seed nodes and only allows 1 node type and Standard: it will have a minimum of 5 seed nodes and allows multiple node types.', default='Basic')
246        c.argument('client_connection_port', options_list=['--client-connection-port', '--client-port'], help='Port used for client connections to the cluster.', default=19000)
247        c.argument('gateway_connection_port', options_list=['--gateway-connection-port', '--gateway-port'], help='Port used for http connections to the cluster.', default=19080)
248        c.argument('client_cert_is_admin', options_list=['--client-cert-is-admin', '--cert-is-admin'], arg_type=get_three_state_flag(), help='Client authentication type.')
249        c.argument('client_cert_thumbprint', options_list=['--client-cert-thumbprint', '--cert-thumbprint'], help='Client certificate thumbprint.')
250        c.argument('client_cert_common_name', options_list=['--client-cert-common-name', '--cert-common-name'], help='Client certificate common name.')
251        c.argument('client_cert_issuer_thumbprint', options_list=['--client-cert-issuer-thumbprint', '--cert-issuer-thumbprint', '--cert-issuer-tp'], nargs='+', help='Space-separated list of issuer thumbprints.')
252        c.argument('upgrade_mode', arg_type=get_enum_type(ClusterUpgradeMode), options_list=['--cluster-upgrade-mode', '--upgrade-mode'],
253                   help='The upgrade mode of the cluster when new Service Fabric runtime version is available '
254                   'Automatic: The cluster will be automatically upgraded to the latest Service Fabric runtime version, upgrade_cadence will determine when the upgrade starts after the new version becomes available.'
255                   'Manual: The cluster will not be automatically upgraded to the latest Service Fabric runtime version. The cluster is upgraded by setting the code_version property in the cluster resource.')
256        c.argument('upgrade_cadence', arg_type=get_enum_type(ClusterUpgradeCadence), options_list=['--cluster-upgrade-cadence', '--upgrade-cadence'],
257                   help='The upgrade mode of the cluster when new Service Fabric runtime version is available '
258                   'Wave0: Cluster upgrade starts immediately after a new version is rolled out. Recommended for Test/Dev clusters.'
259                   'Wave1: Cluster upgrade starts 7 days after a new version is rolled out. Recommended for Pre-prod clusters.'
260                   'Wave2: Cluster upgrade starts 14 days after a new version is rolled out. Recommended for Production clusters.')
261        c.argument('code_version', options_list=['--cluster-code-version', '--code-version'],
262                   help='Cluster service fabric code version. Only use if upgrade mode is Manual.')
263        c.argument('tags', arg_type=tags_type)
264
265    with self.argument_context('sf managed-cluster update') as c:
266        c.argument('client_connection_port', options_list=['--client-connection-port', '--client-port'], help='Port used for client connections to the cluster.')
267        c.argument('gateway_connection_port', options_list=['--gateway-connection-port', '--gateway-port'], help='Port used for http connections to the cluster.')
268        c.argument('dns_name', help='Cluster\'s dns name')
269        c.argument('tags', arg_type=tags_type)
270
271    with self.argument_context('sf managed-cluster client-certificate add') as c:
272        c.argument('is_admin', arg_type=get_three_state_flag(), help='Client authentication type.')
273        c.argument('thumbprint', help='Client certificate thumbprint.')
274        c.argument('common_name', help='Client certificate common name.')
275        c.argument('issuer_thumbprint', nargs='+', help='Space-separated list of issuer thumbprints.')
276
277    with self.argument_context('sf managed-cluster client-certificate delete') as c:
278        c.argument('thumbprint', nargs='+', help='A single or Space-separated list of client certificate thumbprint(s) to be remove.')
279        c.argument('common_name', nargs='+', help='A single or Space-separated list of client certificate common name(s) to be remove.')
280
281    # managed node type
282
283    capacity = CLIArgumentType(
284        options_list=['--capacity'],
285        action=AddNodeTypeCapacityAction,
286        nargs='+',
287        help='Capacity tags applied to the nodes in the node type as key/value pairs, the cluster resource manager uses these tags to understand how much resource a node has. Updating this will override the current values.'
288             'for example: --capacity ClientConnections=65536 param2=value2')
289
290    placement_property = CLIArgumentType(
291        options_list=['--placement-property'],
292        action=AddNodeTypePlacementPropertyAction,
293        nargs='+',
294        help='Placement tags applied to nodes in the node type as key/value pairs, which can be used to indicate where certain services (workload) should run. Updating this will override the current values.'
295             'for example: --placement-property NodeColor=Green SomeProperty=5')
296
297    with self.argument_context('sf managed-node-type') as c:
298        c.argument('node_type_name', options_list=['-n', '--name', '--node-type-name'], help='node type name.')
299        c.argument('instance_count', help='essage = "The number of nodes in the node type.')
300        c.argument('primary', arg_type=get_three_state_flag(), help='Specify if the node type is primary. On this node type will run system services. Only one node type should be marked as primary. Primary node type cannot be deleted or changed for existing clusters.')
301        c.argument('disk_size', type=int, options_list=['--disk-size', '--data-disk-size'], help='Disk size for each vm in the node type in GBs.', default=100)
302        c.argument('disk_type', arg_type=get_enum_type(DiskType), options_list=['--disk-type', '--data-disk-type'],
303                   help='Managed data disk type. IOPS and throughput are given by the disk size, to see more information go to https://docs.microsoft.com/azure/virtual-machines/disks-types. Default StandardSSD_LRS'
304                   'Standard_LRS: Standard HDD locally redundant storage. Best for backup, non-critical, and infrequent access.'
305                   'StandardSSD_LRS: Standard SSD locally redundant storage. Best for web servers, lightly used enterprise applications and dev/test.'
306                   'Premium_LRS: Premium SSD locally redundant storage. Best for production and performance sensitive workloads.')
307        c.argument('application_start_port', options_list=['--application-start-port', '--app-start-port'], help='Application start port of a range of ports.')
308        c.argument('application_end_port', options_list=['--application-end-port', '--app-end-port'], help='Application End port of a range of ports.')
309        c.argument('ephemeral_start_port', help='Ephemeral start port of a range of ports.')
310        c.argument('ephemeral_end_port', help='Ephemeral end port of a range of ports.')
311        c.argument('vm_size', help='The size of virtual machines in the pool. All virtual machines in a pool are the same size.', default='Standard_D2')
312        c.argument('vm_image_publisher', help='The publisher of the Azure Virtual Machines Marketplace image.', default='MicrosoftWindowsServer')
313        c.argument('vm_image_offer', help='The offer type of the Azure Virtual Machines Marketplace image.', default='WindowsServer')
314        c.argument('vm_image_sku', help='The SKU of the Azure Virtual Machines Marketplace image.', default='2019-Datacenter')
315        c.argument('vm_image_version', help='The version of the Azure Virtual Machines Marketplace image. ', default='latest')
316        c.argument('capacity', arg_type=capacity)
317        c.argument('placement_property', arg_type=placement_property)
318        c.argument('is_stateless', arg_type=get_three_state_flag(), help='Indicates if the node type can only host Stateless workloads.', default=False)
319        c.argument('multiple_placement_groups', options_list=['--multiple-placement-groups', '--multi-place-groups'], arg_type=get_three_state_flag(),
320                   help='Indicates if scale set associated with the node type can be composed of multiple placement groups.', default=False)
321
322    with self.argument_context('sf managed-node-type node') as c:
323        c.argument('node_name', nargs='+', help='list of target nodes to perform the operation.')
324        c.argument('force', arg_type=get_three_state_flag(), help='Using this flag will force the operation even if service fabric is unable to disable the nodes. Use with caution as this might cause data loss if stateful workloads are running on the node.')
325
326    with self.argument_context('sf managed-node-type vm-extension') as c:
327        c.argument('extension_name', help='extension name.')
328        c.argument('force_update_tag', help='If a value is provided and is different from the previous value, the extension handler will be forced to update even if the extension configuration has not changed.')
329        c.argument('publisher', help='The name of the extension handler publisher.')
330        c.argument('extension_type', help='Specifies the type of the extension; an example is \"CustomScriptExtension\".')
331        c.argument('type_handler_version', help='Specifies the version of the script handler.')
332        c.argument('auto_upgrade_minor_version', options_list=['--auto-upgrade-minor-version', '--auto-upgrade'], arg_type=get_three_state_flag(), help='Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true.')
333        c.argument('setting', help='Json formatted public settings for the extension.')
334        c.argument('protected_setting', help='The extension can contain either protectedSettings or protectedSettingsFromKeyVault or no protected settings at all.')
335        c.argument('provision_after_extension', options_list=['--provision-after-extension', '--provision-after'], help='Collection of extension names after which this extension needs to be provisioned.')
336
337    with self.argument_context('sf managed-node-type vm-secret') as c:
338        c.argument('source_vault_id', help='Key Vault resource id containing the certificates.')
339        c.argument('certificate_url', help='This is the URL of a certificate that has been uploaded to Key Vault as a secret. For adding a secret to the Key Vault, see [Add a key or secret to the key vault](https://docs.microsoft.com/azure/key-vault/key-vault-get-started/#add). In this case, your certificate needs to be It is the Base64 encoding of the following JSON Object which is encoded in UTF-8: <br><br> {<br>  \"data\":\"<Base64-encoded-certificate>\",<br>  \"dataType\":\"pfx\",<br>  \"password\":\"<pfx-file-password>\"<br>}/')
340        c.argument('certificate_store', help='Specifies the certificate store on the Virtual Machine to which the certificate should be added. The specified certificate store is implicitly in the LocalMachine account.')
341
342    # managed-application-type
343    with self.argument_context('sf managed-application-type') as c:
344        c.argument('application_type_name', options_list=['--name', '--application-type-name'], help='Specify the application type name.')
345        c.argument('tags', arg_type=tags_type)
346
347    # managed-application-type version
348    with self.argument_context('sf managed-application-type version') as c:
349        c.argument('version', arg_type=application_type_version)
350        c.argument('package_url', arg_type=package_url)
351        c.argument('tags', arg_type=tags_type)
352
353    # managed-application
354    service_type_health_policy_map = CLIArgumentType(
355        options_list=['--service-type-health-policy-map', '--service-type-policy'],
356        action=AddServiceTypeHealthPolicyAction,
357        nargs='*',
358        help='Specify the map of the health policy to use for different service types as key/value pairs in the following format: \"ServiceTypeName\"=\"MaxPercentUnhealthyPartitionsPerService,MaxPercentUnhealthyReplicasPerPartition,MaxPercentUnhealthyServices\". '
359        'for example: --service-type-health-policy-map \"ServiceTypeName01\"=\"5,10,5\" \"ServiceTypeName02\"=\"5,5,5\"')
360
361    with self.argument_context('sf managed-application') as c:
362        c.argument('application_name', options_list=['--name', '--application-name'], help='Specify the application name.')
363        c.argument('tags', arg_type=tags_type)
364
365    with self.argument_context('sf managed-application update', validator=validate_update_managed_application) as c:
366        c.argument('application_type_version', arg_type=application_type_version)
367        c.argument('application_parameters', arg_type=application_parameters)
368
369    with self.argument_context('sf managed-application update', arg_group='Upgrade description') as c:
370        c.argument('force_restart', arg_type=get_three_state_flag(),
371                   help='Indicates that the service host restarts even if the upgrade is a configuration-only change.')
372        c.argument('recreate_application', arg_type=get_three_state_flag(),
373                   help='Determines whether the application should be recreated on update. If value=true, the rest of the upgrade policy parameters are not allowed.')
374        c.argument('upgrade_replica_set_check_timeout', options_list=['--upgrade-replica-set-check-timeout', '--replica-check-timeout', '--rep-check-timeout'],
375                   help='Specify the maximum time, in seconds, that Service Fabric waits for a service to reconfigure into a safe state, if not already in a safe state, before Service Fabric proceeds with the upgrade.')
376        c.argument('instance_close_delay_duration', options_list=['--instance-close-delay-duration', '--instance-close-duration', '--close-duration'],
377                   help='Specify the duration in seconds, to wait before a stateless instance is closed, to allow the active requests to drain gracefully. This would be effective when the instance is closing during the application/cluster upgrade, only for those instances which have a non-zero delay duration configured in the service description.')
378        c.argument('failure_action', arg_type=get_enum_type(FailureAction),
379                   help='Specify the action to take if the monitored upgrade fails. The acceptable values for this parameter are Rollback or Manual.')
380        c.argument('upgrade_mode', arg_type=get_enum_type(RollingUpgradeMode),
381                   help='Specify the mode used to monitor health during a rolling upgrade. The values are Monitored, and UnmonitoredAuto.')
382        c.argument('health_check_retry_timeout', options_list=['--hc-retry-timeout', '--health-check-retry-timeout'],
383                   help='Specify the duration, in seconds, after which Service Fabric retries the health check if the previous health check fails.')
384        c.argument('health_check_wait_duration', options_list=['--hc-wait-duration', '--health-check-wait-duration'],
385                   help='Specify the duration, in seconds, that Service Fabric waits before it performs the initial health check after it finishes the upgrade on the upgrade domain.')
386        c.argument('health_check_stable_duration', options_list=['--hc-stable-duration', '--health-check-stable-duration'],
387                   help='Specify the duration, in seconds, that Service Fabric waits in order to verify that the application is stable before moving to the next upgrade domain or completing the upgrade. This wait duration prevents undetected changes of health right after the health check is performed.')
388        c.argument('upgrade_domain_timeout', options_list=['--ud-timeout', '--upgrade-domain-timeout'],
389                   help='Specify the maximum time, in seconds, that Service Fabric takes to upgrade a single upgrade domain. After this period, the upgrade fails.')
390        c.argument('upgrade_timeout',
391                   help='Specify the maximum time, in seconds, that Service Fabric takes for the entire upgrade. After this period, the upgrade fails.')
392        c.argument('consider_warning_as_error', options_list=['--warning-as-error', '--consider-warning-as-error'], arg_type=get_three_state_flag(),
393                   help='Indicates whether to treat a warning health event as an error event during health evaluation.')
394        c.argument('default_service_type_max_percent_unhealthy_partitions_per_service', options_list=['--max-percent-unhealthy-partitions', '--max-unhealthy-parts'],
395                   help='Specify the maximum percent of unhelthy partitions per service allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are from 0 to 100.')
396        c.argument('default_service_type_max_percent_unhealthy_replicas_per_partition', options_list=['--max-percent-unhealthy-replicas', '--max-unhealthy-reps'],
397                   help='Specify the maximum percent of unhelthy replicas per service allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are from 0 to 100.')
398        c.argument('default_service_type_max_percent_unhealthy_services', options_list=['--max-percent-unhealthy-services', '--max-unhealthy-servs'],
399                   help='Specify the maximum percent of unhelthy services allowed by the health policy for the default service type to use for the monitored upgrade. Allowed values are from 0 to 100.')
400        c.argument('service_type_health_policy_map', arg_type=service_type_health_policy_map)
401        c.argument('max_percent_unhealthy_deployed_applications', options_list=['--max-percent-unhealthy-deployed-applications', '--max-percent-unhealthy-apps', '--max-unhealthy-apps'],
402                   help='Specify the maximum percentage of the application instances deployed on the nodes in the cluster that have a health state of error before the application health state for the cluster is error. Allowed values are form 0 to 100.')
403
404    with self.argument_context('sf managed-application create', validator=validate_create_managed_application) as c:
405        c.argument('application_type_name', options_list=['--type-name', '--application-type-name'], help='Specify the application type name.')
406        c.argument('application_type_version', arg_type=application_type_version)
407        c.argument('package_url', arg_type=package_url)
408        c.argument('application_parameters', arg_type=application_parameters)
409
410    # managed-service
411    partition_names = CLIArgumentType(
412        nards="+",
413        help='Specify the array for the names of the partitions. This is only used with Named partition scheme.')
414
415    with self.argument_context('sf managed-service') as c:
416        c.argument('service_name', options_list=['--name', '--service-name'],
417                   help='Specify the name of the service.')
418        c.argument('application_name', options_list=['--application', '--application-name'],
419                   help='Specify the name of the service.')
420        c.argument('tags', arg_type=tags_type)
421
422    with self.argument_context('sf managed-service create', validator=validate_create_managed_service) as c:
423        c.argument('service_type', options_list=['--type', '--service-type'],
424                   help='Specify the service type name of the application, it should exist in the application manifest.')
425        c.argument('default_move_cost', arg_type=get_enum_type(MoveCost),
426                   help='Specify the default cost for a move. Higher costs make it less likely that the Cluster Resource Manager will move the replica when trying to balance the cluster.')
427        c.argument('placement_constraints',
428                   help='Specify the placement constraints as a string. Placement constraints are boolean expressions on node properties and allow for restricting a service to particular nodes based on the service requirements. For example, to place a service on nodes where NodeType is blue specify the following: \"NodeColor == blue)\".')
429        c.argument('service_package_activation_mode', options_list=['--service-package-activation-mode', '--package-activation-mode', '--activation-mode'],
430                   help='Specify the activation mode of the service package.')
431        c.argument('state', arg_type=get_enum_type(ServiceKind), help='Specify if the service is stateless or stateful.')
432        # Stateful arguments
433        c.argument('min_replica_set_size', options_list=['--min-replica-set-size', '--min-replica'], help='Specify the min replica set size for the stateful service.')
434        c.argument('target_replica_set_size', options_list=['--target-replica-set-size', '--target-replica'], help='Specify the target replica set size for the stateful service.')
435        c.argument('has_persisted_state', arg_type=get_three_state_flag(),
436                   help='Determines whether this is a persistent service which stores states on the local disk. If it is then the value of this property is true, if not it is false.')
437        c.argument('service_placement_time_limit', options_list=['--service-placement-time-limit', '--plcmt-time-limit'],
438                   help='Specify the duration for which replicas can stay InBuild before reporting that build is stuck, represented in ISO 8601 format "hh:mm:ss".')
439        c.argument('stand_by_replica_keep_duration', options_list=['--stand-by-replica-keep-duration', '--stand-by-keep-duration', '--keep-duration'],
440                   help='Specify the definition on how long StandBy replicas should be maintained before being removed, represented in ISO 8601 format "hh:mm:ss".')
441        c.argument('quorum_loss_wait_duration', options_list=['--quorum-loss-wait-duration', '--quorum-loss-wait'],
442                   help='Specify the maximum duration for which a partition is allowed to be in a state of quorum loss, represented in ISO 8601 format "hh:mm:ss".')
443        c.argument('replica_restart_wait_duration', options_list=['--replica-restart-wait-duration', '--replica-restart-wait'],
444                   help='Specify the duration between when a replica goes down and when a new replica is created, represented in ISO 8601 format "hh:mm:ss".')
445        # Stateless arguments
446        c.argument('instance_count', help='Specify the instance count for the stateless service. If -1 is used, it means it will run on all the nodes.')
447        c.argument('min_instance_count', help='Specify the minimum number of instances that must be up to meet the EnsureAvailability safety check during operations like upgrade or deactivate node. The actual number that is used is max( MinInstanceCount, ceil( MinInstancePercentage/100.0 * InstanceCount) ). Note, if InstanceCount is set to -1, during MinInstanceCount computation -1 is first converted into the number of nodes on which the instances are allowed to be placed according to the placement constraints on the service.')
448        c.argument('min_instance_percentage', options_list=['--min-instance-percentage', '--min-inst-pct'],
449                   help='Specify the minimum percentage of InstanceCount that must be up to meet the EnsureAvailability safety check during operations like upgrade or deactivate node. The actual number that is used is max( MinInstanceCount, ceil( MinInstancePercentage/100.0 * InstanceCount) ). Note, if InstanceCount is set to -1, during MinInstancePercentage computation, -1 is first converted into the number of nodes on which the instances are allowed to be placed according to the placement constraints on the service. Allowed values are from 0 to 100.')
450        # Partition arguments
451        c.argument('partition_scheme', arg_type=get_enum_type(PartitionScheme),
452                   help='Specify what partition scheme to use. '
453                   'Singleton partitions are typically used when the service does not require any additional routing. '
454                   'UniformInt64 means that each partition owns a range of int64 keys. '
455                   'Named is usually for services with data that can be bucketed, within a bounded set. Some common examples of data fields used as named partition keys would be regions, postal codes, customer groups, or other business boundaries.')
456        c.argument('partition_count',
457                   help='Specify the number of partitions. This is only used with UniformInt64 partition scheme.')
458        c.argument('low_key',
459                   help='Specify the lower bound of the partition key range that should be split between the partition ‘Count’ This is only used with UniformInt64 partition scheme.')
460        c.argument('high_key',
461                   help='Specify the upper bound of the partition key range that should be split between the partition ‘Count’ This is only used with UniformInt64 partition scheme.')
462        c.argument('partition_names', arg_type=partition_names)
463
464    with self.argument_context('sf managed-service update', validator=validate_update_managed_service) as c:
465        c.argument('default_move_cost', arg_type=get_enum_type(MoveCost),
466                   help='Specify the default cost for a move. Higher costs make it less likely that the Cluster Resource Manager will move the replica when trying to balance the cluster.')
467        c.argument('placement_constraints',
468                   help='Specify the placement constraints as a string. Placement constraints are boolean expressions on node properties and allow for restricting a service to particular nodes based on the service requirements. For example, to place a service on nodes where NodeType is blue specify the following: \"NodeColor == blue)\".')
469        # Stateful arguments
470        c.argument('min_replica_set_size', options_list=['--min-replica-set-size', '--min-replica'], help='Specify the min replica set size for the stateful service.')
471        c.argument('target_replica_set_size', options_list=['--target-replica-set-size', '--target-replica'], help='Specify the target replica set size for the stateful service.')
472        c.argument('service_placement_time_limit', options_list=['--service-placement-time-limit', '--plcmt-time-limit'],
473                   help='Specify the duration for which replicas can stay InBuild before reporting that build is stuck, represented in ISO 8601 format "hh:mm:ss".')
474        c.argument('stand_by_replica_keep_duration', options_list=['--stand-by-replica-keep-duration', '--stand-by-keep-duration', '--keep-duration'],
475                   help='Specify the definition on how long StandBy replicas should be maintained before being removed, represented in ISO 8601 format "hh:mm:ss".')
476        c.argument('quorum_loss_wait_duration', options_list=['--quorum-loss-wait-duration', '--quorum-loss-wait'],
477                   help='Specify the maximum duration for which a partition is allowed to be in a state of quorum loss, represented in ISO 8601 format "hh:mm:ss".')
478        c.argument('replica_restart_wait_duration', options_list=['--replica-restart-wait-duration', '--replica-restart-wait'],
479                   help='Specify the duration between when a replica goes down and when a new replica is created, represented in ISO 8601 format "hh:mm:ss".')
480        # Stateless arguments
481        c.argument('instance_count', help='Specify the instance count for the stateless service. If -1 is used, it means it will run on all the nodes.')
482        c.argument('min_instance_count', help='Specify the minimum number of instances that must be up to meet the EnsureAvailability safety check during operations like upgrade or deactivate node. The actual number that is used is max( MinInstanceCount, ceil( MinInstancePercentage/100.0 * InstanceCount) ). Note, if InstanceCount is set to -1, during MinInstanceCount computation -1 is first converted into the number of nodes on which the instances are allowed to be placed according to the placement constraints on the service.')
483        c.argument('min_instance_percentage', options_list=['--min-instance-percentage', '--min-inst-pct'],
484                   help='Specify the minimum percentage of InstanceCount that must be up to meet the EnsureAvailability safety check during operations like upgrade or deactivate node. The actual number that is used is max( MinInstanceCount, ceil( MinInstancePercentage/100.0 * InstanceCount) ). Note, if InstanceCount is set to -1, during MinInstancePercentage computation, -1 is first converted into the number of nodes on which the instances are allowed to be placed according to the placement constraints on the service. Allowed values are from 0 to 100.')
485
486    with self.argument_context('sf managed-service correlation-scheme create', validator=validate_create_managed_service_correlation) as c:
487        c.argument('correlated_service_name', options_list=['--correlated-service-name', '--correlated-name'],
488                   help='Specify the Arm Resource ID of the service that the correlation relationship is established with.')
489        c.argument('scheme', help='Specify the ServiceCorrelationScheme which describes the relationship between this service and the service specified via correlated_service_name.')
490
491    with self.argument_context('sf managed-service correlation-scheme update', validator=validate_update_managed_service_correlation) as c:
492        c.argument('correlated_service_name', options_list=['--correlated-service-name', '--correlated-name'],
493                   help='Specify the Arm Resource ID of the service that the correlation relationship is established with.')
494        c.argument('scheme', help='Specify the ServiceCorrelationScheme which describes the relationship between this service and the service specified via correlated_service_name.')
495
496    with self.argument_context('sf managed-service correlation-scheme delete') as c:
497        c.argument('correlated_service_name', options_list=['--correlated-service-name', '--correlated-name'],
498                   help='Specify the Arm Resource ID of the service that the correlation relationship is established with.')
499
500    with self.argument_context('sf managed-service load-metrics create', validator=validate_create_managed_service_load_metric) as c:
501        c.argument('metric_name', help='Specify the name of the metric.')
502        c.argument('weight', help='Specify the service load metric relative weight, compared to other metrics configured for this service, as a number.')
503        c.argument('primary_default_load', help='Specify the default amount of load, as a number, that this service creates for this metric when it is a Primary replica. Used only for Stateful services.')
504        c.argument('secondary_default_load', help='Specify the default amount of load, as a number, that this service creates for this metric when it is a Secondary replica. Used only for Stateful services.')
505        c.argument('default_load', help='Specify the default amount of load, as a number, that this service creates for this metric. Used only for Stateless services.')
506
507    with self.argument_context('sf managed-service load-metrics update', validator=validate_update_managed_service_load_metric) as c:
508        c.argument('metric_name', help='Specify the name of the metric.')
509        c.argument('weight', help='Specify the service load metric relative weight, compared to other metrics configured for this service, as a number.')
510        c.argument('primary_default_load', help='Specify the default amount of load, as a number, that this service creates for this metric when it is a Primary replica. Used only for Stateful services.')
511        c.argument('secondary_default_load', help='Specify the default amount of load, as a number, that this service creates for this metric when it is a Secondary replica. Used only for Stateful services.')
512        c.argument('default_load', help='Specify the default amount of load, as a number, that this service creates for this metric. Used only for Stateless services.')
513
514    with self.argument_context('sf managed-service load-metrics delete') as c:
515        c.argument('metric_name', help='Specify the name of the metric.')
516
517
518def paramToDictionary(values):
519    params = {}
520    for item in values:
521        key, value = item.split('=', 1)
522        params[key] = value
523    return params
524
525
526# pylint: disable=protected-access
527# pylint: disable=too-few-public-methods
528class AddAppParamsAction(argparse._AppendAction):
529
530    def __call__(self, parser, namespace, values, option_string=None):
531        try:
532            namespace.application_parameters = paramToDictionary(values)
533        except ValueError:
534            raise CLIError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
535
536
537# pylint: disable=protected-access
538# pylint: disable=too-few-public-methods
539class AddServiceTypeHealthPolicyAction(argparse._AppendAction):
540
541    def __call__(self, parser, namespace, values, option_string=None):
542        try:
543            namespace.service_type_health_policy_map = paramToDictionary(values)
544        except ValueError:
545            raise CLIError('usage error: {} KEY=VALUE1,VALUE2,VALUE3 [KEY=VALUE1,VALUE2,VALUE3 ...]'.format(option_string))
546
547
548class ManagedClusterClientCertAddAction(argparse._AppendAction):
549
550    def __call__(self, parser, namespace, values, option_string=None):
551        ClientCertificate = namespace._cmd.get_models('ClientCertificate')
552        try:
553            kwargs = paramToDictionary(values.split())
554            return ClientCertificate(**kwargs)
555        except ValueError:
556            raise CLIError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
557
558
559# pylint: disable=protected-access
560# pylint: disable=too-few-public-methods
561class AddNodeTypeCapacityAction(argparse._AppendAction):
562
563    def __call__(self, parser, namespace, values, option_string=None):
564        try:
565            namespace.capacity = paramToDictionary(values)
566        except ValueError:
567            raise CLIError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
568
569
570# pylint: disable=protected-access
571# pylint: disable=too-few-public-methods
572class AddNodeTypePlacementPropertyAction(argparse._AppendAction):
573
574    def __call__(self, parser, namespace, values, option_string=None):
575        try:
576            namespace.placement_property = paramToDictionary(values)
577        except ValueError:
578            raise CLIError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
579