diff --git a/README.md b/README.md index 37004286..ae47000a 100644 --- a/README.md +++ b/README.md @@ -772,6 +772,30 @@ Default: no default Type: `string` +#### mssql_ha_secondary_role_allow_connections + +A host var it means allow connections type when the replica is secondary role. + +The available values are: `ALL`, `READ_ONLY`, `NO`. + +Default: `ALL` + +Type: `string` + +#### mssql_ha_read_only_routing_list + +Read only routing list in availability group for when the replica is primary role. + +If variable is undefined will not set read only routing list. + +See the doc from microsoft https://learn.microsoft.com/zh-cn/sql/database-engine/availability-groups/windows/configure-read-only-routing-for-an-availability-group-sql-server?view=sql-server-ver16 + +The example: 'node-4','node-6' or ('node-4','node-6') + +Default: `` + +Type: `string` + #### mssql_ha_endpoint_port The TCP port used to replicate data for an Always On availability group. diff --git a/templates/configure_ag.j2 b/templates/configure_ag.j2 index 8f246b54..93d41634 100644 --- a/templates/configure_ag.j2 +++ b/templates/configure_ag.j2 @@ -131,9 +131,14 @@ removing this replica re-create it'; "setting_value":hostvars[item]['__mssql_ha_seeding_mode'] }, "allow_connections":{ - "sql_setting_name": "SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL)", + "sql_setting_name": "SECONDARY_ROLE (ALLOW_CONNECTIONS = " + (hostvars[item]['mssql_ha_secondary_role_allow_connections'] | default('ALL')) + ")", "sys_setting_name": "secondary_role_allow_connections_desc", - "setting_value": "ALL" + "setting_value": (hostvars[item]['mssql_ha_secondary_role_allow_connections'] | default('ALL')) +}, +"read_only_routing_url":{ + "sql_setting_name": "SECONDARY_ROLE (READ_ONLY_ROUTING_URL = " + "N'tcp://" + hostvars[item]['ansible_fqdn'] + ":" + mssql_tcp_port | string + "'" + ")", + "sys_setting_name": "read_only_routing_url", + "setting_value":"N'tcp://" + hostvars[item]['ansible_fqdn'] + ":" + mssql_tcp_port | string + "'" } }) %} {% elif hostvars[item]['mssql_ha_replica_type'] == 'witness' %} @@ -155,7 +160,7 @@ removing this replica re-create it'; WHERE groups.name = '{{ mssql_ha_ag_name }}' AND replicas.replica_server_name = '{{ hostvars[item]['ansible_hostname'] }}' AND -{% if key == 'endpoint_url' %} +{% if key in ['endpoint_url', 'read_only_routing_url'] %} {{ value.sys_setting_name }} = {{ value.setting_value }} {% else %} {{ value.sys_setting_name }} = '{{ value.setting_value }}' @@ -164,24 +169,43 @@ removing this replica re-create it'; BEGIN ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} MODIFY REPLICA ON N'{{ hostvars[item]['ansible_hostname'] }}' WITH ( -{% if key == 'allow_connections' %} +{% if key in ['allow_connections', 'read_only_routing_url'] %} {{ value.sql_setting_name }} {% else %} {{ value.sql_setting_name }} = {{ value.setting_value }} {% endif %} ); PRINT '{{ hostvars[item]['ansible_hostname'] }}: \ -The {{ value.sql_setting_name }} setting on this \ +The +{% if key == 'read_only_routing_url' %} +{{ value.sys_setting_name }} +{% else %} +{{ value.sql_setting_name }} +{% endif %} +setting on this \ {{ hostvars[item]['mssql_ha_replica_type'] }} replica configured successfully'; END ELSE BEGIN PRINT '{{ hostvars[item]['ansible_hostname'] }}: \ -The {{ value.sql_setting_name }} setting on this \ +The +{% if key == 'read_only_routing_url' %} +{{ value.sys_setting_name }} +{% else %} +{{ value.sql_setting_name }} +{% endif %} +setting on this \ {{ hostvars[item]['mssql_ha_replica_type'] }} replica is already set \ correctly, skipping'; END {% endfor %} +{% if not (hostvars[item]['mssql_ha_read_only_routing_list'] | default('')) == '' %} + ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} MODIFY REPLICA ON + N'{{ hostvars[item]['ansible_hostname'] }}' WITH (PRIMARY_ROLE(READ_ONLY_ROUTING_LIST = ( {{ hostvars[item]['mssql_ha_read_only_routing_list'] }} ))) + PRINT '{{ hostvars[item]['ansible_hostname'] }}: \ +The mssql_ha_read_only_routing_list setting on this \ +{{ hostvars[item]['mssql_ha_replica_type'] }} replica configured successfully'; +{% endif %} END {% elif hostvars[item]['mssql_ha_replica_type'] == 'absent' %} IF NOT EXISTS ( @@ -246,7 +270,10 @@ BEGIN AVAILABILITY_MODE = {{ hostvars[item]['__mssql_ha_availability_mode'] }}, FAILOVER_MODE = {{ hostvars[item]['__mssql_ha_failover_mode'] }}, SEEDING_MODE = {{ hostvars[item]['__mssql_ha_seeding_mode'] }}, - SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL) + SECONDARY_ROLE (ALLOW_CONNECTIONS = {{ hostvars[item]['mssql_ha_secondary_role_allow_connections'] | default('ALL') }},READ_ONLY_ROUTING_URL = N'tcp://{{ hostvars[item]['ansible_fqdn'] }}:{{ mssql_tcp_port }}') +{% if not (hostvars[item]['mssql_ha_read_only_routing_list'] | default('')) == '' %} + ,PRIMARY_ROLE(READ_ONLY_ROUTING_LIST = ( {{ hostvars[item]['mssql_ha_read_only_routing_list'] }} )) +{% endif %} {% elif hostvars[item]['mssql_ha_replica_type'] in ['synchronous', 'asynchronous'] %} ), N'{{ hostvars[item]['ansible_hostname'] }}' WITH ( @@ -255,7 +282,10 @@ BEGIN AVAILABILITY_MODE = {{ hostvars[item]['__mssql_ha_availability_mode'] }}, FAILOVER_MODE = {{ hostvars[item]['__mssql_ha_failover_mode'] }}, SEEDING_MODE = {{ hostvars[item]['__mssql_ha_seeding_mode'] }}, - SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL) + SECONDARY_ROLE (ALLOW_CONNECTIONS = {{ hostvars[item]['mssql_ha_secondary_role_allow_connections'] | default('ALL') }},READ_ONLY_ROUTING_URL = N'tcp://{{ hostvars[item]['ansible_fqdn'] }}:{{ mssql_tcp_port }}') +{% if not (hostvars[item]['mssql_ha_read_only_routing_list'] | default('')) == '' %} + ,PRIMARY_ROLE(READ_ONLY_ROUTING_LIST = ( {{ hostvars[item]['mssql_ha_read_only_routing_list'] }} )) +{% endif %} {% elif hostvars[item]['mssql_ha_replica_type'] == 'witness' %} ), N'{{ hostvars[item]['ansible_hostname'] }}' WITH ( diff --git a/tests/test_ag_with_read_only_url_list_allow_connection.yml b/tests/test_ag_with_read_only_url_list_allow_connection.yml new file mode 100644 index 00000000..7681a0c8 --- /dev/null +++ b/tests/test_ag_with_read_only_url_list_allow_connection.yml @@ -0,0 +1,134 @@ +--- +#all: +# hosts: +# 192.168.200.136: +# ansible_user: root +# ansible_ssh_pass: "1q!" +# mssql_ha_replica_type: primary +# mssql_ha_secondary_role_allow_connections: READ_ONLY +# mssql_ha_read_only_routing_list: ('node-5','node-6') +# ha_cluster: +# node_name: node-4 +# pcs_address: node-4 +# corosync_addresses: +# - 192.168.200.136 +# 192.168.200.137: +# ansible_user: root +# ansible_ssh_pass: "1q!" +# mssql_ha_replica_type: synchronous +# mssql_ha_secondary_role_allow_connections: READ_ONLY +# mssql_ha_read_only_routing_list: ('node-4','node-6') +# ha_cluster: +# node_name: node-5 +# pcs_address: node-5 +# corosync_addresses: +# - 192.168.200.137 +# 192.168.200.138: +# ansible_user: root +# ansible_ssh_pass: "1q!" +# mssql_ha_replica_type: synchronous +# mssql_ha_secondary_role_allow_connections: READ_ONLY +# mssql_ha_read_only_routing_list: ('node-4','node-5') +# ha_cluster: +# node_name: node-6 +# pcs_address: node-6 +# corosync_addresses: +# - 192.168.200.138 + + +- name: Test AG with read only URL list allow connection + hosts: all + vars: + mssql_accept_microsoft_odbc_driver_17_for_sql_server_eula: true + mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true + mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true + mssql_accept_microsoft_sql_server_standard_eula: true + mssql_version: 2022 + mssql_password: "p@55w0rD" + mssql_edition: 2Q48Q-PB48J-DRCVN-GB844-X2H4Q + mssql_datadir: "/data/mssql/1433/database/" + mssql_logdir: "/data/mssql/1433/database/" + #mssql_pre_input_sql_content: "USE MASTER;CREATE DATABASE ExampleDB2;BACKUP DATABASE ExampleDB2 TO DISK='nil'with compression;" + mssql_enable_sql_agent: true + mssql_manage_firewall: true + mssql_run_selinux_confined: false + mssql_ha_configure: true + mssql_manage_ha_cluster: true + mssql_ha_prep_for_pacemaker: true + mssql_ha_ag_cluster_type: external + mssql_ha_endpoint_port: 5022 + mssql_ha_cert_name: ExampleCert + mssql_ha_master_key_password: "p@55w0rD1" + mssql_ha_private_key_password: "p@55w0rD2" + mssql_ha_reset_cert: true + mssql_ha_endpoint_name: Example_Endpoint + mssql_ha_ag_name: ExampleAG + mssql_ha_db_names: + - test_1 + mssql_ha_login: pacemakerLogin + mssql_ha_login_password: "p@55w0rD3" + mssql_ha_virtual_ip: 192.168.200.139 + ha_cluster_cluster_name: "{{ mssql_ha_ag_name }}" + ha_cluster_hacluster_password: "p@55w0rD4" + ha_cluster_cluster_properties: + - attrs: + - name: cluster-recheck-interval + value: 2min + - name: start-failure-is-fatal + value: false + - name: stonith-enabled + value: false + ha_cluster_resource_primitives: + - id: ag_cluster + agent: ocf:mssql:ag + instance_attrs: + - attrs: + - name: ag_name + value: "{{ mssql_ha_ag_name }}" + meta_attrs: + - attrs: + - name: failure-timeout + value: 60s + - id: virtualip + agent: ocf:heartbeat:IPaddr2 + instance_attrs: + - attrs: + - name: ip + value: "{{ mssql_ha_virtual_ip }}" + operations: + - action: monitor + attrs: + - name: interval + value: 30s + ha_cluster_resource_clones: + - resource_id: ag_cluster + promotable: yes + meta_attrs: + - attrs: + - name: notify + value: true + ha_cluster_constraints_colocation: + - resource_leader: + id: ag_cluster-clone + role: Promoted + resource_follower: + id: virtualip + options: + - name: score + value: INFINITY + ha_cluster_constraints_order: + - resource_first: + id: ag_cluster-clone + action: promote + resource_then: + id: virtualip + action: start + tasks: +# - name: Set facts to create a test DB on primary as a pre task +# set_fact: +# mssql_pre_input_sql_file: create_ExampleDB.sql +# when: mssql_ha_replica_type == 'primary' + + - name: Run on all hosts to configure HA cluster + include_role: + name: microsoft.sql.server