Dynamic workloads: for Provider Network
This patch add support for dynamic workloads for provider network. 1. provider_netcreate_nova_boot_ping: Creates a provider network and boot VM and ping 2. provider_net_nova_boot_ping: Boot a VM on a existing provider network and ping 3. provider_net_nova_delete: Deletes All VM's on a provider network and then the network It also install scapy on the undercloud Change-Id: Id700c3bcde400398909c8ca9cfd2dd1451b8eb26
This commit is contained in:
parent
cd38bcb85b
commit
76bff93a95
@ -82,6 +82,13 @@
|
||||
- debug: msg="Browbeat directory exists already."
|
||||
when: browbeat_exists.stat.isdir is defined and browbeat_exists.stat.isdir
|
||||
|
||||
- name: install scapy
|
||||
pip:
|
||||
name: "scapy"
|
||||
state: latest
|
||||
become: true
|
||||
when: ansible_distribution_major_version >= '8'
|
||||
|
||||
- name: Clone browbeat on undercloud
|
||||
git:
|
||||
repo: https://github.com/openstack/browbeat.git
|
||||
|
@ -577,12 +577,18 @@ workloads:
|
||||
num_add_subports: 1
|
||||
num_delete_subports_trunks: 1
|
||||
num_delete_subports: 1
|
||||
provider_phys_net: "provider1"
|
||||
iface_name: "ens7f0"
|
||||
iface_mac: "3c:fd:fe:c1:73:40"
|
||||
num_vms_provider_net: 2
|
||||
# workloads can be 'all', a single workload(Eg. : create_delete_servers),
|
||||
# or a comma separated string(Eg. : create_delete_servers,migrate_servers).
|
||||
# Currently supported workloads : create_delete_servers, migrate_servers
|
||||
# create_loadbalancers, pod_fip_simulation, add_subports_to_random_trunks,
|
||||
# delete_subports_from_random_trunks, delete_loadbalancers, delete_members_random_lb
|
||||
# Note: Octavia scenarios are not included in 'all' by default, and have
|
||||
# to be included separately.
|
||||
# create_loadbalancers, delete_loadbalancers, delete_members_random_lb,
|
||||
# pod_fip_simulation, add_subports_to_random_trunks,
|
||||
# delete_subports_from_random_trunks, provider_netcreate_nova_boot_ping,
|
||||
# provider_net_nova_boot_ping, provider_net_nova_delete
|
||||
# Note: Octavia and Provider scenarios are not included in 'all' by default,
|
||||
# and have to be included separately.
|
||||
workloads: all
|
||||
file: rally/rally-plugins/dynamic-workloads/dynamic_workload.yml
|
||||
|
@ -32,3 +32,6 @@ Functions:
|
||||
- _boot_server_with_tag: Boot a server with a tag
|
||||
- _boot_server_with_fip_and_tag: Boot server prepared for SSH actions, with tag
|
||||
- _get_servers_by_tag: Retrieve list of servers based on tag
|
||||
- provider_netcreate_nova_boot_ping: Creates a provider Network and Boots VM and ping
|
||||
- provider_net_nova_boot_ping: Boots a VM and ping on random existing provider network
|
||||
- provider_net_nova_delete: Delete all VM's and provider network
|
||||
|
@ -17,6 +17,7 @@ from rally.task import validation
|
||||
import vm
|
||||
import trunk
|
||||
import octavia
|
||||
import provider_network
|
||||
|
||||
|
||||
@types.convert(octavia_image={"type": "glance_image"}, octavia_flavor={"type": "nova_flavor"})
|
||||
@ -43,13 +44,14 @@ import octavia
|
||||
platform="openstack",
|
||||
)
|
||||
class DynamicWorkload(vm.VMDynamicScenario, trunk.TrunkDynamicScenario,
|
||||
octavia.DynamicOctaviaBase):
|
||||
octavia.DynamicOctaviaBase, provider_network.DynamicProviderNetworkBase):
|
||||
def run(
|
||||
self, smallest_image, smallest_flavor, ext_net_id, num_vms_to_create_for_migration,
|
||||
num_vms_to_migrate, trunk_image, trunk_flavor, num_initial_subports, num_trunk_vms,
|
||||
num_add_subports, num_add_subports_trunks, num_delete_subports, num_delete_subports_trunks,
|
||||
octavia_image, octavia_flavor, user, user_data_file, num_lbs, num_pools,
|
||||
num_clients, delete_num_lbs, delete_num_members, num_create_delete_vms, workloads="all",
|
||||
octavia_image, octavia_flavor, user, user_data_file, num_lbs, num_pools, num_clients,
|
||||
delete_num_lbs, delete_num_members, num_create_delete_vms, provider_phys_net,
|
||||
iface_name, iface_mac, num_vms_provider_net, workloads="all",
|
||||
router_create_args=None, network_create_args=None,
|
||||
subnet_create_args=None, **kwargs):
|
||||
|
||||
@ -85,3 +87,15 @@ class DynamicWorkload(vm.VMDynamicScenario, trunk.TrunkDynamicScenario,
|
||||
|
||||
if "delete_members_random_lb" in workloads_list:
|
||||
self.delete_members_random_lb(delete_num_members)
|
||||
|
||||
if "provider_netcreate_nova_boot_ping" in workloads_list:
|
||||
self.provider_netcreate_nova_boot_ping(smallest_image, smallest_flavor,
|
||||
provider_phys_net, iface_name,
|
||||
iface_mac, num_vms_provider_net)
|
||||
|
||||
if "provider_net_nova_boot_ping" in workloads_list:
|
||||
self.provider_net_nova_boot_ping(provider_phys_net, iface_name, iface_mac,
|
||||
smallest_image, smallest_flavor)
|
||||
|
||||
if "provider_net_nova_delete" in workloads_list:
|
||||
self.provider_net_nova_delete(provider_phys_net)
|
||||
|
@ -60,6 +60,10 @@ BrowbeatPlugin.dynamic_workload:
|
||||
num_create_delete_vms: {{num_create_delete_vms}}
|
||||
num_vms_to_create_for_migration: {{num_vms_to_create_for_migration}}
|
||||
num_vms_to_migrate: {{num_vms_to_migrate}}
|
||||
provider_phys_net: '{{ provider_phys_net }}'
|
||||
iface_name: '{{ iface_name }}'
|
||||
iface_mac: '{{ iface_mac }}'
|
||||
num_vms_provider_net: {{ num_vms_provider_net }}
|
||||
ext_net_id: '{{ext_net_id}}'
|
||||
workloads: '{{workloads}}'
|
||||
runner:
|
||||
|
152
rally/rally-plugins/dynamic-workloads/provider_network.py
Normal file
152
rally/rally-plugins/dynamic-workloads/provider_network.py
Normal file
@ -0,0 +1,152 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import random
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from rally_openstack.scenarios.neutron import utils as neutron_utils
|
||||
import dynamic_utils
|
||||
from rally.task import atomic
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DynamicProviderNetworkBase(dynamic_utils.NovaUtils, neutron_utils.NeutronScenario):
|
||||
|
||||
@atomic.action_timer("neutron.create_network")
|
||||
def _create_provider_network(self, provider_phys_net):
|
||||
"""Create neutron provider network.
|
||||
:param provider_phys_net: provider physical network
|
||||
:returns: neutron network dict
|
||||
"""
|
||||
project_id = self.context["tenant"]["id"]
|
||||
body = {
|
||||
"name": self.generate_random_name(),
|
||||
"tenant_id": project_id,
|
||||
"provider:network_type": "vlan",
|
||||
"provider:physical_network": provider_phys_net
|
||||
}
|
||||
# provider network can be created by admin client only
|
||||
return self.admin_clients("neutron").create_network({"network": body})
|
||||
|
||||
@atomic.action_timer("neutron.show_network")
|
||||
def _show_provider_network(self, provider_network):
|
||||
"""Fetches information of a certain provider network.
|
||||
:param provider_network: provider network object
|
||||
"""
|
||||
|
||||
return self.admin_clients("neutron").show_network(provider_network['network']['id'])
|
||||
|
||||
@atomic.action_timer("neutron.delete_network")
|
||||
def _delete_provider_network(self, provider_network):
|
||||
"""Delete neutron provider network.
|
||||
:param provider_network: provider network object
|
||||
"""
|
||||
|
||||
return self.admin_clients("neutron").delete_network(provider_network['network']['id'])
|
||||
|
||||
def ping_server(self, server, iface_name, iface_mac, network, subnet):
|
||||
"""Ping server created on provider network
|
||||
:param server: server object
|
||||
:param iface_name: interface name
|
||||
:param iface_mac: interface MAC
|
||||
:param network: provider network object
|
||||
:param subnet: subnet object
|
||||
"""
|
||||
|
||||
internal_network = list(server.networks)[0]
|
||||
server_ip = server.addresses[internal_network][0]["addr"]
|
||||
server_mac = server.addresses[internal_network][0]["OS-EXT-IPS-MAC:mac_addr"]
|
||||
gateway = subnet['subnet']['gateway_ip']
|
||||
vlan = network['network']['provider:segmentation_id']
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
file_path = os.path.join(dir_path, "scapy_icmp.py")
|
||||
cmd = ["sudo", file_path, server_ip, server_mac, gateway, iface_mac, iface_name, str(vlan)]
|
||||
proc = subprocess.Popen(cmd)
|
||||
proc.wait()
|
||||
if proc.returncode == 0:
|
||||
LOG.info("Ping to {} is successful".format(server_ip))
|
||||
else:
|
||||
LOG.info("Ping to {} is failed".format(server_ip))
|
||||
|
||||
def provider_netcreate_nova_boot_ping(self, image, flavor, provider_phys_net, iface_name,
|
||||
iface_mac, num_vms_provider_net, router_create_args=None,
|
||||
network_create_args=None,
|
||||
subnet_create_args=None,
|
||||
**kwargs):
|
||||
"""Create provider network, Boot and Ping VM
|
||||
:param image: image ID or image name
|
||||
:param flavor: flavor ID or flavor name
|
||||
:param provider_phys_net: provider physical network
|
||||
:param iface_name: interface name
|
||||
:param iface_mac: interface MAC
|
||||
:param num_vms_provider_net: int, number of vm's to create
|
||||
:param router_create_args: dict, arguments for router creation
|
||||
:param network_create_args: dict, arguments for network creation
|
||||
:param subnet_create_args: dict, arguments for subnet creation
|
||||
:param kwargs: dict, Keyword arguments to function
|
||||
"""
|
||||
|
||||
for _ in range(num_vms_provider_net):
|
||||
provider_network = self._create_provider_network(provider_phys_net)
|
||||
subnet = self._create_subnet(provider_network, subnet_create_args or {})
|
||||
kwargs["nics"] = [{'net-id': provider_network['network']['id']}]
|
||||
tag = "provider_network:"+str(provider_network['network']['id'])
|
||||
server = self._boot_server_with_tag(image, flavor, tag, **kwargs)
|
||||
LOG.info(" Server {} created on provider network {}".format(server, provider_network))
|
||||
self.ping_server(server, iface_name, iface_mac, provider_network, subnet)
|
||||
|
||||
def pick_random_provider_net(self, provider_phys_net, **kwargs):
|
||||
"""Picks random provider network that exists
|
||||
:param provider_phys_net: provider physical network
|
||||
:param kwargs: dict, Keyword arguments to function
|
||||
"""
|
||||
|
||||
kwargs["provider:physical_network"] = provider_phys_net
|
||||
nets = self._list_networks(**kwargs)
|
||||
return self._show_provider_network({'network': random.choice(nets)})
|
||||
|
||||
def provider_net_nova_boot_ping(self, provider_phys_net, iface_name,
|
||||
iface_mac, image, flavor, **kwargs):
|
||||
"""Boot a VM on a random provider network that exists and Ping
|
||||
:param provider_phys_net: provider physical network
|
||||
:param iface_name: interface name
|
||||
:param iface_mac: interface MAC
|
||||
:param image: image ID or image name
|
||||
:param flavor: flavor ID or flavor name
|
||||
:param kwargs: dict, Keyword arguments to function
|
||||
"""
|
||||
|
||||
random_network = self.pick_random_provider_net(provider_phys_net)
|
||||
kwargs["nics"] = [{'net-id': random_network['network']['id']}]
|
||||
tag = "provider_network:"+str(random_network['network']['id'])
|
||||
server = self._boot_server_with_tag(image, flavor, tag, **kwargs)
|
||||
subnet_id = random_network['network']['subnets'][0]
|
||||
subnet = self.admin_clients("neutron").show_subnet(subnet_id)
|
||||
self.ping_server(server, iface_name, iface_mac, random_network, subnet)
|
||||
|
||||
def provider_net_nova_delete(self, provider_phys_net, **kwargs):
|
||||
"""Delete all the VM's on the provider network and then
|
||||
the network.
|
||||
:param provider_phys_net: provider physical network
|
||||
:param kwargs: dict, Keyword arguments to function
|
||||
"""
|
||||
|
||||
random_network = self.pick_random_provider_net(provider_phys_net)
|
||||
kwargs["nics"] = [{'net-id': random_network['network']['id']}]
|
||||
tag = "provider_network:"+str(random_network['network']['id'])
|
||||
servers = self._get_servers_by_tag(tag)
|
||||
for server in servers:
|
||||
self._delete_server(server)
|
||||
self._delete_provider_network(random_network)
|
64
rally/rally-plugins/dynamic-workloads/scapy_icmp.py
Executable file
64
rally/rally-plugins/dynamic-workloads/scapy_icmp.py
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/python3
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
from scapy.all import srp
|
||||
from scapy.all import Ether
|
||||
from scapy.all import ARP
|
||||
from scapy.all import sendp
|
||||
from scapy.all import Dot1Q
|
||||
from scapy.all import IP
|
||||
from scapy.all import ICMP
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _send_icmp(dst_ip, dst_mac, src_ip, src_mac, iface, vlan):
|
||||
# send 1 icmp packet so that dest VM can send ARP packet to resolve gateway
|
||||
sendp(Ether(dst=dst_mac, src=src_mac)/Dot1Q(vlan=int(vlan))/IP(dst=dst_ip)/ICMP(),
|
||||
iface=iface, verbose=0)
|
||||
bcast = "ff:ff:ff:ff:ff:ff"
|
||||
# Send GARP using ARP reply method
|
||||
sendp(Ether(dst=bcast,src=src_mac)/Dot1Q(vlan=int(vlan))/ARP(
|
||||
op=2,psrc=src_ip, hwsrc=src_mac, hwdst=src_mac, pdst=src_ip), iface=iface, verbose=0)
|
||||
# send ICMP and validate reply
|
||||
ans, unans = srp(Ether(dst=dst_mac, src=src_mac)/Dot1Q(vlan=int(vlan))/IP(dst=dst_ip)/ICMP(),
|
||||
iface=iface, timeout=5, verbose=0)
|
||||
if (str(ans).find('ICMP:0') == -1):
|
||||
for snd, rcv in ans:
|
||||
if (rcv.summary().find(dst_ip) != -1):
|
||||
LOG.info("Ping to {} is succesful".format(dst_ip))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main(args):
|
||||
dst_ip, dst_mac, src_ip, src_mac, iface, vlan = args[1:]
|
||||
attempts = 0
|
||||
max_attempts = 120
|
||||
while attempts < max_attempts:
|
||||
if _send_icmp(dst_ip, dst_mac, src_ip, src_mac, iface, vlan):
|
||||
LOG.info("Ping to {} is succesful".format(dst_ip))
|
||||
return 0
|
||||
LOG.info("Ping to {} is failed, attempt {}".format(dst_ip, attempts))
|
||||
attempts += 1
|
||||
time.sleep(5)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
Loading…
x
Reference in New Issue
Block a user