Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 1 | import base64 |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 2 | import multiprocessing as mp |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 3 | import os |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 4 | import random |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 5 | import sys |
| 6 | from typing import Dict, Final, List |
| 7 | |
| 8 | import connection as conn |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 9 | from openstack.exceptions import ResourceFailure |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 10 | from oslo_utils import encodeutils |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 11 | |
| 12 | |
| 13 | compute = conn.cloud.compute |
| 14 | network = conn.cloud.network |
| 15 | volume = conn.cloud.volume |
| 16 | |
| 17 | CLIENTS_COUNT: Final[int] = conn.FIO_CLIENTS_COUNT |
| 18 | CLIENT_NAME_MASK: Final[str] = conn.FIO_CLIENT_NAME_MASK |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 19 | AA_SERVER_GROUP_NAME: Final[str] = conn.FIO_AA_SERVER_GROUP_NAME |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 20 | UBUNTU_IMAGE_NAME: Final[str] = conn.UBUNTU_IMAGE_NAME |
| 21 | |
| 22 | VOL_NAME_MASK: Final[str] = conn.FIO_VOL_NAME_MASK |
| 23 | VOL_SIZE: Final[int] = conn.FIO_VOL_SIZE |
| 24 | VOL_TYPE: Final[str] = conn.FIO_VOL_TYPE |
| 25 | VOL_MOUNTPOINT: Final[str] = conn.FIO_VOL_MOUNTPOINT |
| 26 | |
| 27 | FLAVOR_NAME: Final[str] = conn.FIO_FLAVOR_NAME |
| 28 | FLAVOR_RAM: Final[int] = conn.FIO_FLAVOR_RAM |
| 29 | FLAVOR_CPUS: Final[int] = conn.FIO_FLAVOR_CPUS |
| 30 | FLAVOR_DISK: Final[int] = conn.FIO_FLAVOR_DISK |
| 31 | |
| 32 | NET_NAME: Final[str] = conn.FIO_NET_NAME |
| 33 | ROUTER_NAME: Final[str] = conn.FIO_ROUTER_NAME |
| 34 | FLOATING_NET_NAME = conn.FLOATING_NET_NAME |
| 35 | SUBNET_NAME = conn.FIO_SUBNET_NAME |
| 36 | SUBNET_RANGE = conn.FIO_SUBNET_RANGE |
| 37 | MTU_SIZE = conn.MTU_SIZE |
| 38 | NET_IPV4 = conn.NET_IPV4 |
| 39 | |
| 40 | KEYPAIR_NAME: Final[str] = conn.FIO_KEYPAIR_NAME |
| 41 | PRIVATE_KEYPAIR_FILE: Final[str] = conn.PRIVATE_KEYPAIR_FILE |
| 42 | |
| 43 | SG_NAME: Final[str] = conn.FIO_SG_NAME |
| 44 | HV_SUFFIX: Final[str] = conn.HV_SUFFIX |
| 45 | CLOUD_NAME: Final[str] = conn.CLOUD_NAME |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 46 | CONCURRENCY: Final[int] = conn.CONCURRENCY |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 47 | |
| 48 | NODES: Final[List[str]] = [] |
| 49 | SKIP_NODES: Final[List[str]] = [] |
| 50 | |
| 51 | |
| 52 | SG_ALLOW_ALL_RULES: Final[List[Dict]] = [ |
| 53 | { |
| 54 | 'remote_ip_prefix': '0.0.0.0/0', |
| 55 | 'protocol': 'icmp', |
| 56 | 'port_range_max': None, |
| 57 | 'port_range_min': None, |
| 58 | 'ethertype': 'IPv4' |
| 59 | }, |
| 60 | { |
| 61 | 'remote_ip_prefix': '0.0.0.0/0', |
| 62 | 'protocol': 'tcp', |
| 63 | 'port_range_max': 65535, |
| 64 | 'port_range_min': 1, |
| 65 | 'ethertype': 'IPv4' |
| 66 | }, |
| 67 | { |
| 68 | 'remote_ip_prefix': '0.0.0.0/0', |
| 69 | 'protocol': 'udp', |
| 70 | 'port_range_max': 65535, |
| 71 | 'port_range_min': 1, |
| 72 | 'ethertype': 'IPv4' |
| 73 | } |
| 74 | ] |
| 75 | |
| 76 | |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 77 | def create_fio_client( |
| 78 | image_id: str, flavor_id: str, networks: List, |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 79 | key_name: str, security_groups: List, server_group_id: str, |
| 80 | user_data: str |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 81 | ) -> None: |
| 82 | rand_name = str(random.randint(1, 0x7fffffff)) |
| 83 | vm_name = f"{CLIENT_NAME_MASK}-{rand_name}" |
| 84 | |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 85 | kwargs = {} |
| 86 | if user_data: |
| 87 | kwargs['user_data'] = user_data |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 88 | vm = compute.create_server( |
| 89 | name=vm_name, |
| 90 | image_id=image_id, |
| 91 | flavor_id=flavor_id, |
| 92 | networks=networks, |
| 93 | key_name=key_name, |
| 94 | security_groups=security_groups, |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 95 | scheduler_hints={'group': server_group_id}, |
| 96 | **kwargs) |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 97 | try: |
| 98 | vm = compute.wait_for_server(vm, wait=180) |
| 99 | print(f"Fio client '{vm.name}' is created on '{vm.compute_host}' node") |
| 100 | # Stop and exit if any of the servers creation failed (for any reason) |
| 101 | except ResourceFailure as e: |
| 102 | print( |
| 103 | f"Fio client '{vm.name}' creation failed with '{e.message}'" |
| 104 | " error.") |
| 105 | conn.delete_server(vm) |
| 106 | sys.exit(0) |
| 107 | |
| 108 | # Create a volume of the given type |
| 109 | vol_name = f"{VOL_NAME_MASK}-{rand_name}" |
| 110 | vol = volume.create_volume( |
| 111 | name=vol_name, size=VOL_SIZE, volume_type=VOL_TYPE) |
| 112 | try: |
| 113 | vol = volume.wait_for_status(vol, status='available') |
| 114 | print(f"Volume '{vol.name}' is created") |
| 115 | # Delete a volume if its creation failed and switch to next |
| 116 | # fio client VM |
| 117 | except ResourceFailure as e: |
| 118 | print( |
| 119 | f"Volume '{vol.name}' creation failed with '{e.message}' " |
| 120 | "error.") |
| 121 | conn.delete_volume(vol) |
| 122 | |
| 123 | # Attach the volume to the fio client |
| 124 | compute.create_volume_attachment(vm, volume=vol) |
| 125 | try: |
| 126 | vol = volume.wait_for_status(vol, status='in-use') |
| 127 | print(f"Volume '{vol.name}' is attached to '{vm.name}' fio client") |
| 128 | # Delete a volume if attachment failed and switch to next |
| 129 | # fio client VM |
| 130 | except ResourceFailure as e: |
| 131 | print( |
| 132 | f"Volume '{vol.name}' attachment failed with '{e.message}' " |
| 133 | "error.") |
| 134 | conn.delete_volume(vol) |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 135 | |
| 136 | |
| 137 | if __name__ == "__main__": |
| 138 | # Check if any fio servers already exist on the cloud |
| 139 | servers = compute.servers(details=False, name=CLIENT_NAME_MASK) |
| 140 | srvrs = list(servers) |
| 141 | if srvrs: |
| 142 | names = [s.name for s in srvrs] |
| 143 | print("The following servers already exist in the cloud:") |
| 144 | print(*names, sep='\n') |
| 145 | sys.exit(0) |
| 146 | |
| 147 | # Create fio sg if needed |
Ievgeniia Zadorozhna | 09f06c2 | 2023-08-29 19:39:27 +0300 | [diff] [blame] | 148 | project_id = conn.cloud.current_project_id |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 149 | sg = network.find_security_group(SG_NAME, project_id=project_id) |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 150 | if not sg: |
| 151 | sg = network.create_security_group(name=SG_NAME) |
| 152 | # Add 'allow-all' kind of rules to the security group |
| 153 | pairs = [ |
| 154 | (r, d) for r in SG_ALLOW_ALL_RULES for d in ('ingress', 'egress')] |
| 155 | for (rule, direction) in pairs: |
| 156 | network.create_security_group_rule( |
| 157 | security_group_id=sg.id, direction=direction, **rule) |
| 158 | |
| 159 | # Create fio keypair if needed |
| 160 | kp = compute.find_keypair(KEYPAIR_NAME) |
| 161 | if not kp: |
| 162 | kp = compute.create_keypair(name=KEYPAIR_NAME) |
| 163 | with open(PRIVATE_KEYPAIR_FILE, 'w') as f: |
| 164 | f.write("{}".format(kp.private_key)) |
| 165 | |
| 166 | os.chmod(PRIVATE_KEYPAIR_FILE, 0o400) |
| 167 | |
| 168 | # Create fio flavor if needed |
| 169 | flavor = compute.find_flavor(FLAVOR_NAME) |
| 170 | if not flavor: |
| 171 | flavor = compute.create_flavor( |
| 172 | name=FLAVOR_NAME, ram=FLAVOR_RAM, |
| 173 | vcpus=FLAVOR_CPUS, disk=FLAVOR_DISK) |
| 174 | |
| 175 | # Set image property to enable virtio-net multique in created servers |
| 176 | img = compute.find_image(UBUNTU_IMAGE_NAME) |
| 177 | compute.set_image_metadata(img.id, hw_vif_multiqueue_enabled='true') |
| 178 | |
| 179 | # Create fio router if needed |
| 180 | fip_net = network.find_network(FLOATING_NET_NAME) |
| 181 | router = network.find_router(ROUTER_NAME) |
| 182 | if not router: |
| 183 | router = network.create_router( |
| 184 | name=ROUTER_NAME, external_gateway_info={'network_id': fip_net.id}) |
| 185 | |
| 186 | # Create fio net/subnet if needed |
| 187 | fio_net = network.find_network(NET_NAME) |
| 188 | if not fio_net: |
| 189 | fio_net = network.create_network( |
| 190 | name=NET_NAME, |
| 191 | availability_zone_hints=['nova'], |
| 192 | # mtu=MTU_SIZE, |
| 193 | shared=False, |
| 194 | port_security_enabled=True) |
| 195 | fio_subnet = network.create_subnet( |
| 196 | name=SUBNET_NAME, |
| 197 | network_id=fio_net.id, |
| 198 | cidr=SUBNET_RANGE, |
| 199 | ip_version=NET_IPV4) |
| 200 | # Add fio net to fio router |
| 201 | fio_net_port = network.add_interface_to_router( |
| 202 | router.id, subnet_id=fio_subnet.id) |
| 203 | |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 204 | # Create fio server group with anti-affinity scheduling policy |
| 205 | server_group = compute.find_server_group( |
| 206 | AA_SERVER_GROUP_NAME, all_projects=True) |
| 207 | if not server_group: |
| 208 | server_group = compute.create_server_group( |
| 209 | name=AA_SERVER_GROUP_NAME, policies=['soft-anti-affinity']) |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 210 | |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 211 | # Prepare user data for fio client VMs |
| 212 | udata = None |
| 213 | with open('user_data.sh', 'r') as script: |
| 214 | f = encodeutils.safe_encode(script.read().encode('utf-8')) |
| 215 | udata = base64.b64encode(f).decode('utf-8') |
| 216 | |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 217 | vm_kwargs = dict( |
| 218 | image_id=img.id, |
| 219 | flavor_id=flavor.id, |
| 220 | networks=[{'uuid': fio_net.id}], |
| 221 | key_name=KEYPAIR_NAME, |
| 222 | security_groups=[{'name': SG_NAME}], |
Dmitriy Kruglov | d3b2bb9 | 2023-12-07 13:26:31 +0100 | [diff] [blame^] | 223 | server_group_id=server_group.id, |
| 224 | user_data=udata) |
Dmitriy Kruglov | 6146cb4 | 2023-04-10 00:13:59 +0200 | [diff] [blame] | 225 | |
Dmitriy Kruglov | 090cb19 | 2023-07-04 22:10:50 +0200 | [diff] [blame] | 226 | # Create fio client VMs in parallel in batches of CONCURRENCY size |
| 227 | with mp.Pool(processes=CONCURRENCY) as pool: |
| 228 | results = [ |
| 229 | pool.apply_async(create_fio_client, kwds=vm_kwargs) |
| 230 | for _ in range(CLIENTS_COUNT)] |
| 231 | # Wait for batch of fio client VMs to be created |
| 232 | _ = [r.get() for r in results] |