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