diff --git a/plugin.spec b/plugin.spec new file mode 100644 index 00000000000..fb889ee9bfe --- /dev/null +++ b/plugin.spec @@ -0,0 +1,81 @@ +--- +config: + entry_point: ./tools/ovn_migration/infrared/tripleo-ovn-migration/main.yml + plugin_type: install +subparsers: + tripleo-ovn-migration: + description: Migrate an existing TripleO overcloud from Neutron ML2OVS plugin to OVN + include_groups: ["Ansible options", "Inventory", "Common options", "Answers file"] + groups: + - title: Containers + options: + registry-namespace: + type: Value + help: The alternative docker registry namespace to use for deployment. + + registry-prefix: + type: Value + help: The images prefix + + registry-tag: + type: Value + help: The images tag + + registry-mirror: + type: Value + help: The alternative docker registry to use for deployment. + + - title: Deployment Description + options: + version: + type: Value + help: | + The product version + Numbers are for OSP releases + Names are for RDO releases + If not given, same version of the undercloud will be used + choices: + - "7" + - "8" + - "9" + - "10" + - "11" + - "12" + - "13" + - "14" + - "15" + - "16" + - kilo + - liberty + - mitaka + - newton + - ocata + - pike + - queens + - rocky + - stein + - train + install_from_package: + type: Bool + help: Install python-neutron-ovn-migration-tool rpm + default: True + + dvr: + type: Bool + help: If the deployment is to be dvr or not + default: False + + create_resources: + type: Bool + help: Create resources to measure downtime + default: True + + external_network: + type: Value + help: External network name to use + default: public + + image_name: + type: Value + help: Image name to use + default: cirros-0.3.5-x86_64-disk.img diff --git a/setup.cfg b/setup.cfg index 457eec936b9..4eaa846ce8e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,9 @@ data_files = etc/api-paste.ini etc/rootwrap.conf etc/neutron/rootwrap.d = etc/neutron/rootwrap.d/* + share/ansible/neutron-ovn-migration/playbooks = tools/ovn_migration/tripleo_environment/playbooks/* +scripts = + tools/ovn_migration/tripleo_environment/ovn_migration.sh [entry_points] wsgi_scripts = diff --git a/tools/ovn_migration/README.rst b/tools/ovn_migration/README.rst new file mode 100644 index 00000000000..9d1e5487f84 --- /dev/null +++ b/tools/ovn_migration/README.rst @@ -0,0 +1,43 @@ +Migration from ML2/OVS to ML2/OVN +================================= + +Proof-of-concept ansible script for migrating an OpenStack deployment +that uses ML2/OVS to OVN. + +If you have a tripleo ML2/OVS deployment then please see the folder +``tripleo_environment`` + +Prerequisites: + +1. Ansible 2.2 or greater. + +2. ML2/OVS must be using the OVS firewall driver. + +To use: + +1. Create an ansible inventory with the expected set of groups and variables + as indicated by the hosts-sample file. + +2. Run the playbook:: + + $ ansible-playbook migrate-to-ovn.yml -i hosts + +Testing Status: + +- Tested on an RDO cloud on CentOS 7.3 based on Ocata. +- The cloud had 3 controller nodes and 6 compute nodes. +- Observed network downtime was 10 seconds. +- The "--forks 10" option was used with ansible-playbook to ensure + that commands could be run across the entire environment in parallel. + +MTU: + +- If migrating an ML2/OVS deployment using VXLAN tenant networks + to an OVN deployment using Geneve for tenant networks, we have + an unresolved issue around MTU. The VXLAN overhead is 30 bytes. + OVN with Geneve has an overhead of 38 bytes. We need the tenant + networks MTU adjusted for OVN and then we need all VMs to receive + the updated MTU value through DHCP before the migration can take + place. For testing purposes, we've just hacked the Neutron code + to indicate that the VXLAN overhead was 38 bytes instead of 30, + bypassing the issue at migration time. diff --git a/tools/ovn_migration/hosts.sample b/tools/ovn_migration/hosts.sample new file mode 100644 index 00000000000..0e1842903b1 --- /dev/null +++ b/tools/ovn_migration/hosts.sample @@ -0,0 +1,37 @@ +# All controller nodes running OpenStack control services, particularly +# neutron-api. Also indicate which controller you'd like to have run +# the OVN central control services. +[controller] +overcloud-controller-0 ovn_central=true +overcloud-controller-1 +overcloud-controller-2 + +# All compute nodes. We will replace the openvswitch agent +# with ovn-controller on these nodes. +# +# The ovn_encap_ip variable should be filled in with the IP +# address that other compute hosts should use as the tunnel +# endpoint for tunnels to that host. +[compute] +overcloud-novacompute-0 ovn_encap_ip=192.0.2.10 +overcloud-novacompute-1 ovn_encap_ip=192.0.2.11 +overcloud-novacompute-2 ovn_encap_ip=192.0.2.12 +overcloud-novacompute-3 ovn_encap_ip=192.0.2.13 +overcloud-novacompute-4 ovn_encap_ip=192.0.2.14 +overcloud-novacompute-5 ovn_encap_ip=192.0.2.15 + +# Configure bridge mappings to be used on compute hosts. +[compute:vars] +ovn_bridge_mappings=net1:br-em1 +is_compute_node=true + +[overcloud:children] +controller +compute + +# Fill in "ovn_db_ip" with an IP address on a management network +# that the controller and compute nodes should reach. This address +# should not be reachable otherwise. +[overcloud:vars] +ovn_db_ip=192.0.2.50 +remote_user=heat-admin diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/README.rst b/tools/ovn_migration/infrared/tripleo-ovn-migration/README.rst new file mode 100644 index 00000000000..1f1d04dbf58 --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/README.rst @@ -0,0 +1,33 @@ +Infrared plugin to carry out migration from ML2/OVS to OVN +========================================================== + +This is an infrared plugin which can be used to carry out the migration +from ML2/OVS to OVN if the tripleo was deployed using infrared. +See http://infrared.readthedocs.io/en/stable/index.html for more information. + +Before using this plugin, first deploy an ML2/OVS overcloud and then: + +1. On your undercloud, install python-neutron-ovn-migration-tool package (https://trunk.rdoproject.org/centos7-master/current/) + You also need to install python-neutron and python3-openvswitch packages. + +2. Run :: + $infrared plugin add "https://github.com/openstack/neutron.git" + +3. Start migration by running:: + + $infrared tripleo-ovn-migration --version 13|14 \ +--registry-namespace \ +--registry-tag \ +--registry-prefix + +Using this as a standalone playbook for tripleo deployments +=========================================================== +It is also possible to use the playbook main.yml with tripleo deployments. +In order to use this: + +1. Create hosts inventory file like below +[undercloud] +undercloud_ip ansible_ssh_user=stack + +2. Run the playbook as: +ansible-playbook main.yml -i hosts -e install_from_package=True -e registry_prefix=centos-binary -e registry_namespace=docker.io/tripleomaster -e registry_localnamespace=192.168.24.1:8787/tripleomaster -e registry_tag=current-tripleo-rdo diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/main.yml b/tools/ovn_migration/infrared/tripleo-ovn-migration/main.yml new file mode 100644 index 00000000000..bf92c1d1de4 --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/main.yml @@ -0,0 +1,194 @@ +# Playbook which preps migration and then invokes the migration script. +- name: Install migration tool + hosts: undercloud + become: true + tasks: + - name: Install python 3 virtualenv and neutron ovn migration tool + yum: + name: + - python3-virtualenv + - python3-neutron-ovn-migration-tool + state: present + + - name: Set host_key_checking to False in ansible.cfg + ini_file: + path=/etc/ansible/ansible.cfg + section=defaults + option=host_key_checking + value=False + ignore_errors: yes + +- name: Prepare for migration + hosts: undercloud + tasks: + - name: Set ovn migration working dir + set_fact: + ovn_migration_working_dir: /home/stack/ovn_migration + + - name: Delete temp file directory if present + file: + state: absent + path: "{{ ovn_migration_working_dir }}" + + - name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_working_dir }}" + + - name: Set the docker registry information + block: + - name: Get the docker registry info (infrared deployment) + block: + - name: Set is_infrard deployment + set_fact: + is_infrared: True + + - name: Save the docker reg + set_fact: + container_image_prepare: + namespace: "{{ install.get('registry', {}).namespace|default(False)|ternary(install.get('registry', {}).namespace, install.get('registry', {}).mirror + '/' + 'rhosp' + install.version) }}" + prefix: "{{ install.registry.prefix|default('openstack') }}" + tag: "{{ install.registry.tag|default('') }}" + local_namespace: "{{ install.registry.local|default('') }}" + is_dvr: "{{ install.dvr }}" + when: + - install is defined + + - name: Get the docker registry info (tripleo deployment) + block: + - name: Set is_infrard deployment + set_fact: + is_infrared: False + + - name: Save the docker reg + set_fact: + container_image_prepare: + namespace: "{{ registry_namespace }}" + local_namespace: "{{ registry_localnamespace }}" + prefix: "{{ registry_prefix }}" + tag: "{{ registry_tag }}" + is_dvr: "{{ dvr }}" + when: + - install is not defined + + - name: Prepare for migration + include_role: + name: prepare-migration + vars: + infrared_deployment: "{{ is_infrared }}" + registry_namespace: "{{ container_image_prepare['namespace'] }}" + image_prefix: "{{ container_image_prepare['prefix'] }}" + image_tag: "{{ container_image_prepare['tag'] }}" + local_namespace: "{{ container_image_prepare['local_namespace'] }}" + is_dvr: "{{ container_image_prepare['is_dvr'] }}" + +- name: Boot few VMs to measure downtime + hosts: undercloud + tasks: + - name: Check if need to create resources + block: + - name: Set create_vms (infrared) + set_fact: + create_vms: "{{ install.create_resources }}" + when: + - install is defined + + - name: Set create_vms (tripleo deployment) + set_fact: + create_vms: "{{ create_resources }}" + when: + - install is not defined + + - name: Create few resources + block: + - name: Set the public network name (infrared deployment) + set_fact: + public_net: "{{ install.external_network }}" + when: install is defined + + - name: Set the public network name (Tripleo deployment) + set_fact: + public_net: "{{ external_network }}" + when: install is not defined + + - name: Set the image name (infrared deployment) + set_fact: + image_to_boot: "{{ install.image_name }}" + when: install is defined + + - name: Set the image name(Tripleo deployment) + set_fact: + image_to_boot: "{{ image_name }}" + when: install is not defined + + - name: Create resources + include_role: + name: create-resources + vars: + public_network_name: "{{ public_net }}" + image_name: "{{ image_to_boot }}" + ovn_migration_temp_dir: /home/stack/ovn_migration + overcloudrc: /home/stack/overcloudrc + when: + - create_vms|bool + +- name: Kick start the migration + hosts: undercloud + tasks: + #TODO: Get the working dir from the param + - name: Starting migration block + block: + - name: Set ovn migration working dir + set_fact: + ovn_migration_working_dir: /home/stack/ovn_migration + + - name: Copy the playbook files into ovn_migration working dir + command: cp -rf /usr/share/ansible/neutron-ovn-migration/playbooks {{ ovn_migration_working_dir }} + + - name: Set the public network name (infrared deployment) + set_fact: + public_network: "{{ install.external_network }}" + when: install is defined + + - name: Set the public network name (Tripleo deployment) + set_fact: + public_network: "{{ external_network }}" + when: install is not defined + + - name: Create ovn migration script + template: + src: templates/start-ovn-migration.sh.j2 + dest: "{{ ovn_migration_working_dir }}/start-ovn-migration.sh" + mode: 0755 + + - name: Generate inventory file for ovn migration + shell: + set -o pipefail && + {{ ovn_migration_working_dir }}/start-ovn-migration.sh generate-inventory 2>&1 > {{ ovn_migration_working_dir}}/generate-inventory.log + + - name: Set MTU T1 + shell: + set -o pipefail && + {{ ovn_migration_working_dir }}/start-ovn-migration.sh setup-mtu-t1 2>&1 > {{ ovn_migration_working_dir}}/setup-mtu-t1.log + + - name: Reduce mtu of the pre migration networks + shell: + set -o pipefail && + {{ ovn_migration_working_dir }}/start-ovn-migration.sh reduce-mtu 2>&1 > {{ ovn_migration_working_dir}}/reduce-mtu.log + + - name: Start the migration process + shell: + set -o pipefail && + {{ ovn_migration_working_dir }}/start-ovn-migration.sh start-migration 2>&1 + > {{ ovn_migration_working_dir}}/start-ovn-migration.sh.log + + - name: Stop pinger if started + shell: + echo "exit" > {{ ovn_migration_working_dir}}/_pinger_cmd.txt + always: + - name: Fetch ovn_migration log directory + synchronize: + src: "{{ ovn_migration_working_dir }}" + dest: "{{ inventory_dir }}" + mode: pull + when: install is defined diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/defaults/main.yml b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/defaults/main.yml new file mode 100644 index 00000000000..5fcb312c9ce --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/defaults/main.yml @@ -0,0 +1,9 @@ +--- + +public_network_name: "{{ public_network_name }}" +create_resource_script: create-resources.sh.j2 +ovn_migration_temp_dir: "{{ ovn_migration_temp_dir }}" +image_name: "{{ image_name }}" +server_user_name: "{{ server_user_name }}" +overcloudrc: "{{ overcloudrc }}" +resource_suffix: pinger diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/tasks/main.yml b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/tasks/main.yml new file mode 100644 index 00000000000..4cfd9032e8a --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/tasks/main.yml @@ -0,0 +1,33 @@ +- name: Delete temp file directory if present + file: + state: absent + path: "{{ ovn_migration_temp_dir }}" + +- name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_temp_dir }}" + +- name: Generate resource creation script + template: + src: create-resources.sh.j2 + dest: "{{ ovn_migration_temp_dir }}/create-resources.sh" + mode: 0744 + +- name: Creating pre pre migration resources + shell: > + set -o pipefail && + {{ ovn_migration_temp_dir }}/create-resources.sh 2>&1 > + {{ ovn_migration_temp_dir }}/create-resources.sh.log + changed_when: true + +- name: Generate pinger script + template: + src: start-pinger.sh.j2 + dest: "{{ ovn_migration_temp_dir }}/start-pinger.sh" + mode: 0744 + +- name: Start pinger in background + shell: > + nohup {{ ovn_migration_temp_dir }}/start-pinger.sh /dev/null 2>&1 & + changed_when: False diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/create-resources.sh.j2 b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/create-resources.sh.j2 new file mode 100644 index 00000000000..1cc095d3e2d --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/create-resources.sh.j2 @@ -0,0 +1,153 @@ +#!/bin/bash + +set -x + +source {{ overcloudrc }} + +image_name={{ image_name }} +openstack image show $image_name +if [ "$?" != "0" ] +then + if [ ! -f cirros-0.4.0-x86_64-disk.img ] + then + curl -Lo cirros-0.4.0-x86_64-disk.img https://github.com/cirros-dev/cirros/releases/download/0.4.0/cirros-0.4.0-x86_64-disk.img + fi + + openstack image create "cirros-ovn-migration-{{ resource_suffix }}" --file cirros-0.4.0-x86_64-disk.img \ +--disk-format qcow2 --container-format bare --public + image_name="cirros-ovn-migration-{{ resource_suffix }}" +fi + + +openstack flavor create ovn-migration-{{ resource_suffix }} --ram 1024 --disk 1 --vcpus 1 + +openstack keypair create ovn-migration-{{ resource_suffix }} --private-key {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key + +openstack security group create ovn-migration-sg-{{ resource_suffix }} + +openstack security group rule create --ingress --protocol icmp ovn-migration-sg-{{ resource_suffix }} + +openstack security group rule create --ingress --protocol tcp --dst-port 22 ovn-migration-sg-{{ resource_suffix }} + +openstack network create ovn-migration-net-{{ resource_suffix }} + +neutron net-update ovn-migration-net-{{ resource_suffix }} --mtu 1442 + +openstack subnet create --network ovn-migration-net-{{ resource_suffix }} --subnet-range 172.168.168.0/24 ovn-migration-subnet-{{ resource_suffix }} + +num_hypervisors=`openstack hypervisor stats show | grep count | awk '{print $4}'` + +openstack server create --flavor ovn-migration-{{ resource_suffix }} --image $image_name \ +--key-name ovn-migration-{{ resource_suffix }} \ +--nic net-id=ovn-migration-net-{{ resource_suffix }} \ +--security-group ovn-migration-sg-{{ resource_suffix }} \ +--min $num_hypervisors --max $num_hypervisors \ +ovn-migration-server-{{ resource_suffix }} + + +openstack router create ovn-migration-router-{{ resource_suffix }} + +openstack router set --external-gateway {{ public_network_name }} ovn-migration-router-{{ resource_suffix }} + +openstack router add subnet ovn-migration-router-{{ resource_suffix }} ovn-migration-subnet-{{ resource_suffix }} + +for i in $(seq 1 $num_hypervisors) +do + num_attempts=0 + while true + do + openstack server show ovn-migration-server-{{ resource_suffix }}-$i -c status | grep ACTIVE + if [ "$?" == "0" ]; then + break + fi + sleep 5 + num_attempts=$((num_attempts+1)) + if [ $num_attempts -gt 24 ] + then + echo "VM is not up even after 2 minutes. Something is wrong" + exit 1 + fi + done + + vm_ip=`openstack server show ovn-migration-server-{{ resource_suffix }}-$i -c addresses | grep addresses | awk '{ split($4, ip, "="); print ip[2]}'` + port_id=`openstack port list | grep $vm_ip | awk '{print $2}'` + + # Wait till the port is ACTIVE + echo "Wait till the port is ACTIVE" + port_status=`openstack port show $port_id -c status | grep status | awk '{print $4}'` + + num_attempts=0 + while [ "$port_status" != "ACTIVE" ] + do + num_attempts=$((num_attempts+1)) + sleep 5 + port_status=`openstack port show $port_id -c status | grep status | awk '{print $4}'` + echo "Port status = $port_status" + if [ $num_attempts -gt 24 ] + then + echo "Port is not up even after 2 minutes. Something is wrong" + exit 1 + fi + done + + echo "VM is up and the port is ACTIVE" + + server_ip=`openstack floating ip create --port $port_id \ +{{ public_network_name }} -c floating_ip_address | grep floating_ip_address \ +| awk '{print $4'}` + + echo $server_ip >> {{ ovn_migration_temp_dir }}/server_fips + + # Wait till the VM allows ssh connections + + vm_status="down" + num_attempts=0 + while [ "$vm_status" != "up" ] + do + num_attempts=$((num_attempts+1)) + sleep 5 + openstack console log show ovn-migration-server-{{ resource_suffix }}-$i | grep "login:" + if [ "$?" == "0" ] + then + vm_status="up" + else + if [ $num_attempts -gt 60 ] + then + echo "VM is not up with login prompt even after 5 minutes. Something is wrong." + # Even though something seems wrong, lets try and ping. + break + fi + fi + done +done + +chmod 0600 {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key + + +for server_ip in `cat {{ ovn_migration_temp_dir }}/server_fips` +do + num_attempts=0 + vm_reachable="false" + while [ "$vm_reachable" != "true" ] + do + num_attempts=$((num_attempts+1)) + sleep 1 + ping -c 3 $server_ip + if [ "$?" == "0" ] + then + vm_reachable="true" + else + if [ $num_attempts -gt 60 ] + then + echo "VM is not pingable. Something is wrong." + exit 1 + fi + fi + done + + ssh -i {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null cirros@$server_ip date +done + +echo "Done with the resource creation : exiting" +exit 0 diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/start-pinger.sh.j2 b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/start-pinger.sh.j2 new file mode 100644 index 00000000000..d21025eb5fa --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/create-resources/templates/start-pinger.sh.j2 @@ -0,0 +1,58 @@ +#!/bin/bash + +set -x + +echo "creating virtualenv in {{ ovn_migration_temp_dir }}/pinger_venv" +virtualenv {{ ovn_migration_temp_dir }}/pinger_venv +source {{ ovn_migration_temp_dir }}/pinger_venv/bin/activate +pip install --upgrade pip +pip install sh + +cat > {{ ovn_migration_temp_dir }}/pinger.py <<-EOF +import sh +import sys +import time + + +def main(ips): + run_cmds = [] + for ip in ips: + ip_out_file = "{{ ovn_migration_temp_dir }}/" + ip.replace('.', '_') + '_ping.out' + run_cmds.append(sh.ping('-i', '1', ip, _out=ip_out_file, _bg=True)) + + if not run_cmds: + return + + while True: + try: + cmd_file = open("{{ ovn_migration_temp_dir }}/_pinger_cmd.txt", "r") + cmd = cmd_file.readline() + if cmd.startswith("exit"): + break + cmd_file.close() + except IOError: + time.sleep(3) + continue + + for p in run_cmds: + p.signal(2) + p.wait() + + +if __name__ == '__main__': + main(sys.argv[1:]) + +EOF + +pinger_ips="" +for ip in `cat {{ ovn_migration_temp_dir }}/server_fips` +do + pinger_ips="$pinger_ips $ip" +done + +echo "pinger ips = $pinger_ips" + +echo "calling pinger.py" +python {{ ovn_migration_temp_dir }}/pinger.py $pinger_ips + +echo "Exiting..." diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/defaults/main.yml b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/defaults/main.yml new file mode 100644 index 00000000000..a5ab93c44fd --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/defaults/main.yml @@ -0,0 +1,7 @@ +--- + +infrared_deployment: False +registry_namespace: docker.io/tripleomaster +local_namespace: 192.168.24.1:8787/tripleomaster +image_tag: current-tripleo-rdo +image_prefix: centos-binary- diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/tasks/main.yml b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/tasks/main.yml new file mode 100644 index 00000000000..6174c6b516b --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/roles/prepare-migration/tasks/main.yml @@ -0,0 +1,181 @@ +- name: Copy overcloud deploy script to overcloud-deploy-ovn.sh + block: + - name: Check if overcloud_deploy.sh is present or not + stat: + path: ~/overcloud_deploy.sh + register: deploy_file + + - name: Set the ml2ovs overcloud deploy script file name + set_fact: + overcloud_deploy_script: '~/overcloud_deploy.sh' + when: deploy_file.stat.exists|bool + + - name: Check if overcloud-deploy.sh is present + stat: + path: ~/overcloud-deploy.sh + register: deploy_file_2 + when: not deploy_file.stat.exists|bool + + - name: Set the ml2ovs overcloud deploy script file name + set_fact: + overcloud_deploy_script: '~/overcloud-deploy.sh' + when: + - not deploy_file.stat.exists|bool + - deploy_file_2.stat.exists|bool + + - name: Copy overcloud deploy script to overcloud-deploy-ovn.sh + command: cp -f {{ overcloud_deploy_script }} ~/overcloud-deploy-ovn.sh + when: infrared_deployment|bool + +- name: set overcloud deploy ovn script + set_fact: + overcloud_deploy_ovn_script: '~/overcloud-deploy-ovn.sh' + +- name: Set docker images environment file + set_fact: + output_env_file: /home/stack/docker-images-ovn.yaml + +- name: Get the proper neutron-ovn-ha.yaml path + stat: + path: /usr/share/openstack-tripleo-heat-templates/environments/services/neutron-ovn-ha.yaml + register: ovn_env_path + +- name: Set the neutron-ovn-dvr-ha.yaml file path if dvr + set_fact: + neutron_ovn_env_path: /usr/share/openstack-tripleo-heat-templates/environments/services/neutron-ovn-dvr-ha.yaml + when: is_dvr|bool + +- name: Set the neutron-ovn-ha.yaml file path if not dvr + set_fact: + neutron_ovn_env_path: /usr/share/openstack-tripleo-heat-templates/environments/services/neutron-ovn-ha.yaml + when: not is_dvr|bool + +- name: Construct overcloud-deploy-ovn.sh script for infrared deployments + lineinfile: + dest: "{{ overcloud_deploy_ovn_script }}" + line: "{{ item }} \\" + insertbefore: "^--log-file.*" + with_items: + - "-e {{ neutron_ovn_env_path }}" + - "-e /home/stack/ovn-extras.yaml" + - "-e {{ output_env_file }}" + when: + - infrared_deployment|bool + +- name: Construct overcloud-deploy-ovn.sh script for tripleo deployments + template: + src: templates/overcloud-deploy-ovn.sh.j2 + dest: ~/overcloud-deploy-ovn.sh + mode: 0744 + when: + - not infrared_deployment|bool + +- name: Set image tag (infrared deployment) + block: + - name: Get puddle version + shell: cat containers-prepare-parameter.yaml | grep -v _tag | grep tag | awk '{print $2}' + ignore_errors: True + register: core_puddle_version + + - name: Set image tag from puddle version + set_fact: + docker_image_tag: "{{ core_puddle_version.stdout }}" + + - name: Get registry namespace + shell: cat containers-prepare-parameter.yaml | grep -v _namespace | grep namespace | awk '{print $2}' + ignore_errors: True + register: reg_ns + + - name: Set registry namespace + set_fact: + reg_namespace: "{{ reg_ns.stdout }}" + + - debug: + msg: "{{ core_puddle_version.stdout }}" + + - debug: + msg: "{{ docker_image_tag }}" + + - debug: + msg: "{{ reg_namespace }}" + when: infrared_deployment|bool + +- name: Set image tag (tripleo deployment) + set_fact: + docker_image_tag: "{{ image_tag }}" + when: + - not infrared_deployment|bool + + +- name: Generate ovn container images + shell: | + echo "container_images:" > ~/ovn_container_images.yaml + args: + creates: ~/ovn_container_images.yaml + +- name: Add ovn container images to ovn_container_images.yaml + lineinfile: + dest: ~/ovn_container_images.yaml + line: "- imagename: {{ reg_namespace }}/{{ image_prefix }}-{{ item }}:{{ docker_image_tag }}" + with_items: + - "ovn-northd" + - "ovn-controller" + - "neutron-server-ovn" + - "neutron-metadata-agent-ovn" + +- name: Generate docker images environment file + shell: | + echo "parameter_defaults:" > ~/docker-images-ovn.yaml + changed_when: False + +- name: Set the local namespace + block: + - name: Extract the local namespace + shell: | + set -exo pipefail + source ~/stackrc + openstack overcloud plan export overcloud + mkdir -p /tmp/oc_plan + mv overcloud.tar.gz /tmp/oc_plan/ + cd /tmp/oc_plan + tar xvf overcloud.tar.gz + reg=`cat /tmp/oc_plan/environments/containers-default-parameters.yaml | grep ContainerNeutronApiImage | awk '{ split($2, image , "/"); print image[1] }'` + namespace=`cat /tmp/oc_plan/environments/containers-default-parameters.yaml | grep ContainerNeutronApiImage | awk '{ split($2, image , "/"); print image[2] }'` + echo $reg/$namespace > /tmp/_reg_namespace + rm -rf /tmp/oc_plan + + - name: Get the local namespace + command: cat /tmp/_reg_namespace + register: local_ns + + - name: Set the local registry + set_fact: + local_registry: "{{ local_ns.stdout }}" + when: + - local_namespace == '' + +- name: Set the local namespace + set_fact: + local_registry: "{{ local_namespace }}" + when: + - local_namespace != '' + +- name: Add ovn container images to docker images environment file + lineinfile: + dest: ~/docker-images-ovn.yaml + line: " {{ item.name }}: {{ local_registry }}/{{ image_prefix }}-{{ item.image_name }}:{{ docker_image_tag }}" + with_items: + - { name: ContainerNeutronApiImage, image_name: neutron-server-ovn} + - { name: ContainerNeutronConfigImage, image_name: neutron-server-ovn} + - { name: ContainerOvnMetadataImage, image_name: neutron-metadata-agent-ovn} + - { name: ContainerOvnControllerImage, image_name: ovn-controller} + - { name: ContainerOvnControllerConfigImage, image_name: ovn-controller} + - { name: ContainerOvnDbsImage, image_name: ovn-northd} + - { name: ContainerOvnDbsConfigImage, image_name: ovn-northd} + +- name: Upload the ovn container images to the local registry + shell: | + source /home/stack/stackrc + openstack tripleo container image prepare --environment-file /home/stack/containers-prepare-parameter.yaml + become: yes + changed_when: False diff --git a/tools/ovn_migration/infrared/tripleo-ovn-migration/templates/start-ovn-migration.sh.j2 b/tools/ovn_migration/infrared/tripleo-ovn-migration/templates/start-ovn-migration.sh.j2 new file mode 100644 index 00000000000..699409ef27f --- /dev/null +++ b/tools/ovn_migration/infrared/tripleo-ovn-migration/templates/start-ovn-migration.sh.j2 @@ -0,0 +1,7 @@ +#!/bin/bash + +export PUBLIC_NETWORK_NAME={{ public_network }} +# TODO: Get this from the var +export OPT_WORKDIR=/home/stack/ovn_migration + +/usr/bin/ovn_migration.sh $1 diff --git a/tools/ovn_migration/migrate-to-ovn.yml b/tools/ovn_migration/migrate-to-ovn.yml new file mode 100644 index 00000000000..f49e6ac34f2 --- /dev/null +++ b/tools/ovn_migration/migrate-to-ovn.yml @@ -0,0 +1,204 @@ +# Migrate a Neutron deployment using ML2/OVS to OVN. +# +# See hosts-sample for expected contents of the ansible inventory. + +--- +- hosts: compute + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Ensure OVN packages are installed on compute nodes. + yum: + name: openvswitch-ovn-host + state: present + # TODO to make ansible-lint happy, all of these commands should be conditionally run + # only if the config value needs to be changed. + - name: Configure ovn-encap-type. + command: "ovs-vsctl set open . external_ids:ovn-encap-type=geneve" + changed_when: false + - name: Configure ovn-encap-ip. + command: "ovs-vsctl set open . external_ids:ovn-encap-ip={{ ovn_encap_ip }}" + changed_when: false + - name: Configure ovn-remote. + command: "ovs-vsctl set open . external_ids:ovn-remote=tcp:{{ ovn_db_ip }}:6642" + changed_when: false + # TODO We could discover the appropriate value for ovn-bridge-mappings based on + # the openvswitch agent configuration instead of requiring it to be configured + # in the inventory. + - name: Configure ovn-bridge-mappings. + command: "ovs-vsctl set open . external_ids:ovn-bridge-mappings={{ ovn_bridge_mappings }}" + changed_when: false + - name: Get hostname + command: hostname -f + register: hostname + check_mode: no + changed_when: false + - name: Set host name + command: "ovs-vsctl set Open_vSwitch . external-ids:hostname={{ hostname.stdout }}" + changed_when: false + # TODO ansible has an "iptables" module, but it does not allow you specify a "rule number" + # which we require here. + - name: Open Geneve UDP port for tunneling. + command: iptables -I INPUT 10 -m state --state NEW -p udp --dport 6081 -j ACCEPT + changed_when: false + - name: Persist our iptables changes after a reboot + shell: iptables-save > /etc/sysconfig/iptables.save + args: + creates: /etc/sysconfig/iptables.save + # TODO Remove this once the metadata API is supported. + # https://bugs.launchpad.net/networking-ovn/+bug/1562132 + - name: Force config drive until the metadata API is supported. + ini_file: + dest: /etc/nova/nova.conf + section: DEFAULT + option: force_config_drive + value: true + - name: Restart nova-compute service to reflect force_config_drive value. + systemd: + name: openstack-nova-compute + state: restarted + enabled: yes + +- hosts: controller + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Ensure OVN packages are installed on the central OVN host. + when: ovn_central is defined + yum: + name: openvswitch-ovn-central + state: present + # TODO Set up SSL for OVN databases + # TODO ansible has an "iptables" module, but it does not allow you specify a "rule number" + # which we require here. + - name: Open OVN database ports. + command: "iptables -I INPUT 10 -m state --state NEW -p tcp --dport {{ item }} -j ACCEPT" + with_items: [ 6641, 6642 ] + changed_when: False + - name: Persist our iptables changes after a reboot + shell: iptables-save > /etc/sysconfig/iptables.save + args: + creates: /etc/sysconfig/iptables.save + # TODO Integrate HA support for the OVN control services. + - name: Start ovn-northd and the OVN databases. + when: ovn_central is defined + systemd: + name: ovn-northd + state: started + enabled: yes + - name: Enable remote access to the northbound database. + command: "ovn-nbctl set-connection ptcp:6641:{{ ovn_db_ip }}" + when: ovn_central is defined + changed_when: False + - name: Enable remote access to the southbound database. + command: "ovn-sbctl set-connection ptcp:6642:{{ ovn_db_ip }}" + when: ovn_central is defined + changed_when: False + - name: Update Neutron configuration files + ini_file: dest={{ item.dest }} section={{ item.section }} option={{ item.option }} value={{ item.value }} + with_items: + - { dest: '/etc/neutron/neutron.conf', section: 'DEFAULT', option: 'service_plugins', value: 'qos,ovn-router' } + - { dest: '/etc/neutron/neutron.conf', section: 'DEFAULT', option: 'notification_drivers', value: 'ovn-qos' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ml2', option: 'mechanism_drivers', value: 'ovn' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ml2', option: 'type_drivers', value: 'geneve,vxlan,vlan,flat' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ml2', option: 'tenant_network_types', value: 'geneve' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ml2_type_geneve', option: 'vni_ranges', value: '1:65536' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ml2_type_geneve', option: 'max_header_size', value: '38' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'ovn_nb_connection', value: '"tcp:{{ ovn_db_ip }}:6641"' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'ovn_sb_connection', value: '"tcp:{{ ovn_db_ip }}:6642"' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'ovsdb_connection_timeout', value: '180' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'neutron_sync_mode', value: 'repair' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'ovn_l3_mode', value: 'true' } + - { dest: '/etc/neutron/plugins/ml2/ml2_conf.ini', section: 'ovn', option: 'vif_type', value: 'ovs' } + - name: Note that API downtime begins now. + debug: + msg: NEUTRON API DOWNTIME STARTING NOW FOR THIS HOST + - name: Shut down neutron-server so that we can begin data sync to OVN. + systemd: + name: neutron-server + state: stopped + +- hosts: controller + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Sync Neutron state to OVN. + when: ovn_central is defined + command: neutron-ovn-db-sync-util --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/ml2_conf.ini + +- hosts: overcloud + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Note that data plane imact starts now. + debug: + msg: DATA PLANE IMPACT BEGINS NOW. + - name: Stop metadata, DHCP, L3 and openvswitch agent if needed. + systemd: name={{ item.name }} state={{ item.state }} enabled=no + with_items: + - { name: 'neutron-metadata-agent', state: 'stopped' } + - { name: 'neutron-dhcp-agent', state: 'stopped' } + - { name: 'neutron-l3-agent', state: 'stopped' } + - { name: 'neutron-openvswitch-agent', state: 'stopped' } + +- hosts: compute + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Note that data plane is being restored. + debug: + msg: DATA PLANE IS NOW BEING RESTORED. + - name: Delete br-tun as it is no longer used. + command: "ovs-vsctl del-br br-tun" + changed_when: false + - name: Reset OpenFlow protocol version before ovn-controller takes over. + with_items: [ br-int, br-ex ] + command: "ovs-vsctl set Bridge {{ item }} protocols=[]" + ignore_errors: True + changed_when: false + - name: Start ovn-controller. + systemd: + name: ovn-controller + state: started + enabled: yes + +- hosts: controller + remote_user: "{{ remote_user }}" + become: true + tasks: + # TODO The sync util scheduling gateway routers depends on this patch: + # https://review.openstack.org/#/c/427020/ + # If the patch is not merged, this command is harmless, but the gateway + # routers won't get scheduled until later when neutron-server starts. + - name: Schedule gateway routers by running the sync util. + when: ovn_central is defined + command: neutron-ovn-db-sync-util --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/ml2_conf.ini + changed_when: false + - name: Configure node for hosting gateway routers for external connectivity. + command: "ovs-vsctl set open . external_ids:ovn-cms-options=enable-chassis-as-gw" + changed_when: false + +- hosts: overcloud + remote_user: "{{ remote_user }}" + become: true + tasks: + # TODO Make this smarter so that it only deletes net namespaces that were + # # created by neutron. In the simple case, this is fine, but will break + # # once containers are in use on the overcloud. + - name: Delete network namespaces. + command: ip -all netns delete + changed_when: false + +- hosts: controller + remote_user: "{{ remote_user }}" + become: true + tasks: + - name: Note that the Neutron API is coming back online. + debug: + msg: THE NEUTRON API IS NOW BEING RESTORED. + - name: Start neutron-server. + systemd: + name: neutron-server + state: started + +# TODO In our grenade script we had to restart rabbitmq. Is that needed? diff --git a/tools/ovn_migration/tripleo_environment/ovn_migration.sh b/tools/ovn_migration/tripleo_environment/ovn_migration.sh new file mode 100644 index 00000000000..e0fbf8ed0c2 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/ovn_migration.sh @@ -0,0 +1,349 @@ +#!/bin/bash + +# Copyright 2018 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +# With LANG set to everything else than C completely undercipherable errors +# like "file not found" and decoding errors will start to appear during scripts +# or even ansible modules +LANG=C + +# Complete stackrc file path. +: ${STACKRC_FILE:=~/stackrc} + +# Complete overcloudrc file path. +: ${OVERCLOUDRC_FILE:=~/overcloudrc} + +# overcloud deploy script for OVN migration. +: ${OVERCLOUD_OVN_DEPLOY_SCRIPT:=~/overcloud-deploy-ovn.sh} + +: ${OPT_WORKDIR:=$PWD} +: ${PUBLIC_NETWORK_NAME:=public} +: ${IMAGE_NAME:=cirros} +: ${SERVER_USER_NAME:=cirros} +: ${VALIDATE_MIGRATION:=True} +: ${DHCP_RENEWAL_TIME:=30} + + +check_for_necessary_files() { + if [ ! -e hosts_for_migration ]; then + echo "hosts_for_migration ansible inventory file not present" + echo "Please run ./ovn_migration.sh generate-inventory" + exit 1 + fi + + # Check if the user has generated overcloud-deploy-ovn.sh file + # If it is not generated. Exit + if [ ! -e $OVERCLOUD_OVN_DEPLOY_SCRIPT ]; then + echo "overcloud deploy migration script :" \ + "$OVERCLOUD_OVN_DEPLOY_SCRIPT is not present. Please" \ + "make sure you generate that file before running this" + exit 1 + fi + + cat $OVERCLOUD_OVN_DEPLOY_SCRIPT | grep neutron-ovn >/dev/null + if [ "$?" == "1" ]; then + echo "OVN t-h-t environment file seems to be missing in \ +$OVERCLOUD_OVN_DEPLOY_SCRIPT. Please check the $OVERCLOUD_OVN_DEPLOY_SCRIPT \ +file again." + exit 1 + fi + + cat $OVERCLOUD_OVN_DEPLOY_SCRIPT | grep \$HOME/ovn-extras.yaml >/dev/null + check1=$? + cat $OVERCLOUD_OVN_DEPLOY_SCRIPT | grep $HOME/ovn-extras.yaml >/dev/null + check2=$? + + if [[ "$check1" == "1" && "$check2" == "1" ]]; then + echo "ovn-extras.yaml file is missing in "\ + "$OVERCLOUD_OVN_DEPLOY_SCRIPT. Please add it "\ + "as \" -e \$HOME/ovn-extras.yaml\"" + exit 1 + fi +} + +get_host_ip() { + inventory_file=$1 + host_name=$2 + ip=`jq -r --arg role _meta --arg hostname $host_name 'to_entries[] | select(.key == $role) | .value.hostvars[$hostname].management_ip' $inventory_file` + if [[ "x$ip" == "x" ]] || [[ "x$ip" == "xnull" ]]; then + # This file does not provide translation from the hostname to the IP, or + # we already have an IP (Queens backwards compatibility) + echo $host_name + else + echo $ip + fi +} + +get_role_hosts() { + inventory_file=$1 + role_name=$2 + roles=`jq -r \.$role_name\.children\[\] $inventory_file` + for role in $roles; do + # During the rocky cycle the format changed to have .value.hosts + hosts=`jq -r --arg role "$role" 'to_entries[] | select(.key == $role) | .value.hosts[]' $inventory_file` + if [[ "x$hosts" == "x" ]]; then + # But we keep backwards compatibility with nested childrens (Queens) + hosts=`jq -r --arg role "$role" 'to_entries[] | select(.key == $role) | .value.children[]' $inventory_file` + + for host in $hosts; do + HOSTS="$HOSTS `jq -r --arg host "$host" 'to_entries[] | select(.key == $host) | .value.hosts[0]' $inventory_file`" + done + else + HOSTS="${hosts} ${HOSTS}" + fi + done + echo $HOSTS +} + +# Generate the ansible.cfg file +generate_ansible_config_file() { + + cat > ansible.cfg <<-EOF +[defaults] +forks=50 +become=True +callback_whitelist = profile_tasks +host_key_checking = False +gathering = smart +fact_caching = jsonfile +fact_caching_connection = ./ansible_facts_cache +fact_caching_timeout = 0 + +#roles_path = roles:... + +[ssh_connection] +control_path = %(directory)s/%%h-%%r +ssh_args = -o ControlMaster=auto -o ControlPersist=270s -o ServerAliveInterval=30 -o GSSAPIAuthentication=no +retries = 3 + +EOF +} + +# Generate the inventory file for ansible migration playbook. +generate_ansible_inventory_file() { + echo "Generating the inventory file for ansible-playbook" + source $STACKRC_FILE + echo "[ovn-dbs]" > hosts_for_migration + ovn_central=True + /usr/bin/tripleo-ansible-inventory --list > /tmp/ansible-inventory.txt + # We want to run ovn_dbs where neutron_api is running + OVN_DBS=$(get_role_hosts /tmp/ansible-inventory.txt neutron_api) + for node_name in $OVN_DBS; do + node_ip=$(get_host_ip /tmp/ansible-inventory.txt $node_name) + node="$node_name ansible_host=$node_ip" + if [ "$ovn_central" == "True" ]; then + ovn_central=False + node="$node_name ansible_host=$node_ip ovn_central=true" + fi + echo $node ansible_ssh_user=heat-admin ansible_become=true >> hosts_for_migration + done + + echo "" >> hosts_for_migration + echo "[ovn-controllers]" >> hosts_for_migration + + # We want to run ovn-controller where OVS agent was running before the migration + OVN_CONTROLLERS=$(get_role_hosts /tmp/ansible-inventory.txt neutron_ovs_agent) + for node_name in $OVN_CONTROLLERS; do + node_ip=$(get_host_ip /tmp/ansible-inventory.txt $node_name) + echo $node_name ansible_host=$node_ip ansible_ssh_user=heat-admin ansible_become=true ovn_controller=true >> hosts_for_migration + done + rm -f /tmp/ansible-inventory.txt + echo "" >> hosts_for_migration + + cat >> hosts_for_migration << EOF + +[overcloud-controllers:children] +ovn-dbs + +[overcloud:children] +ovn-controllers +ovn-dbs + +EOF + add_group_vars() { + + cat >> hosts_for_migration << EOF + +[$1:vars] +remote_user=heat-admin +public_network_name=$PUBLIC_NETWORK_NAME +image_name=$IMAGE_NAME +working_dir=$OPT_WORKDIR +server_user_name=$SERVER_USER_NAME +validate_migration=$VALIDATE_MIGRATION +overcloud_ovn_deploy_script=$OVERCLOUD_OVN_DEPLOY_SCRIPT +overcloudrc=$OVERCLOUDRC_FILE +ovn_migration_backups=/var/lib/ovn-migration-backup +EOF + } + + add_group_vars overcloud + add_group_vars overcloud-controllers + + + echo "***************************************" + cat hosts_for_migration + echo "***************************************" + echo "Generated the inventory file - hosts_for_migration" + echo "Please review the file before running the next command - setup-mtu-t1" +} + +# Check if the public network exists, and if it has floating ips available + +oc_check_public_network() { + + source $OVERCLOUDRC_FILE + openstack network show $PUBLIC_NETWORK_NAME 1>/dev/null || { + echo "ERROR: PUBLIC_NETWORK_NAME=${PUBLIC_NETWORK_NAME} can't be accessed by the" + echo " admin user, please fix that before continuing." + exit 1 + } + + ID=$(openstack floating ip create $PUBLIC_NETWORK_NAME -c id -f value) || { + echo "ERROR: PUBLIC_NETWORK_NAME=${PUBLIC_NETWORK_NAME} doesn't have available" + echo " floating ips. Make sure that your public network has at least one" + echo " floating ip available for the admin user." + exit 1 + } + + openstack floating ip delete $ID 2>/dev/null 1>/dev/null + return $? +} + + +# Check if the neutron networks MTU has been updated to geneve MTU size or not. +# We donot want to proceed if the MTUs are not updated. +oc_check_network_mtu() { + source $OVERCLOUDRC_FILE + neutron-ovn-migration-mtu verify mtu + return $? +} + +setup_mtu_t1() { + # Run the ansible playbook to reduce the DHCP T1 parameter in + # dhcp_agent.ini in all the overcloud nodes where dhcp agent is running. + ansible-playbook -vv $OPT_WORKDIR/playbooks/reduce-dhcp-renewal-time.yml \ + -i hosts_for_migration -e working_dir=$OPT_WORKDIR \ + -e renewal_time=$DHCP_RENEWAL_TIME + rc=$? + return $rc +} + +reduce_network_mtu () { + source $OVERCLOUDRC_FILE + oc_check_network_mtu + if [ "$?" != "0" ]; then + # Reduce the network mtu + neutron-ovn-migration-mtu update mtu + rc=$? + + if [ "$rc" != "0" ]; then + echo "Reducing the network mtu's failed. Exiting." + exit 1 + fi + fi + + return $rc +} + +start_migration() { + source $STACKRC_FILE + echo "Starting the Migration" + ansible-playbook -vv $OPT_WORKDIR/playbooks/ovn-migration.yml \ + -i hosts_for_migration -e working_dir=$OPT_WORKDIR \ + -e public_network_name=$PUBLIC_NETWORK_NAME \ + -e image_name=$IMAGE_NAME \ + -e overcloud_ovn_deploy_script=$OVERCLOUD_OVN_DEPLOY_SCRIPT \ + -e server_user_name=$SERVER_USER_NAME \ + -e overcloudrc=$OVERCLOUDRC_FILE \ + -e validate_migration=$VALIDATE_MIGRATION $* + + rc=$? + return $rc +} + +print_usage() { + +cat << EOF + +Usage: + + Before running this script, please refer to the migration guide for +complete details. This script needs to be run in 5 steps. + + Step 1 -> ovn_migration.sh generate-inventory + + Generates the inventory file + + Step 2 -> ovn_migration.sh setup-mtu-t1 + + Sets the DHCP renewal T1 to 30 seconds. After this step you will + need to wait at least 24h for the change to be propagated to all + VMs. This step is only necessary for VXLAN or GRE based tenant + networking. + + Step 3 -> You need to wait at least 24h based on the default configuration + of neutron for the DHCP T1 parameter to be propagated, please + refer to documentation. WARNING: this is very important if you + are using VXLAN or GRE tenant networks. + + Step 4 -> ovn_migration.sh reduce-mtu + + Reduces the MTU of the neutron tenant networks networks. This + step is only necessary for VXLAN or GRE based tenant networking. + + Step 5 -> ovn_migration.sh start-migration + + Starts the migration to OVN. + +EOF + +} + +command=$1 + +ret_val=0 +case $command in + generate-inventory) + oc_check_public_network + generate_ansible_inventory_file + generate_ansible_config_file + ret_val=$? + ;; + + setup-mtu-t1) + check_for_necessary_files + setup_mtu_t1 + ret_val=$?;; + + reduce-mtu) + check_for_necessary_files + reduce_network_mtu + ret_val=$?;; + + start-migration) + oc_check_public_network + check_for_necessary_files + shift + start_migration $* + ret_val=$? + ;; + + *) + print_usage;; +esac + +exit $ret_val diff --git a/tools/ovn_migration/tripleo_environment/playbooks/ovn-migration.yml b/tools/ovn_migration/tripleo_environment/playbooks/ovn-migration.yml new file mode 100644 index 00000000000..2e4a7b420c1 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/ovn-migration.yml @@ -0,0 +1,110 @@ +# This is the playbook used by ovn-migration.sh. + +# +# Pre migration and validation tasks will make sure that the initial cloud +# is functional, and will create resources which will be checked after +# migration. +# + +- name: Pre migration and validation tasks + hosts: localhost + roles: + - pre-migration + tags: + - pre-migration + +# +# This step is executed before migration, and will backup some config +# files related to containers before those get lost. +# + +- name: Backup tripleo container config files on the nodes + hosts: ovn-controllers + roles: + - backup + tags: + - setup + +# +# TripleO / Director is executed to deploy ovn using "br-migration" for the +# dataplane, while br-int is left intact to avoid dataplane disruption. +# + +- name: Set up OVN and configure it using tripleo + hosts: localhost + roles: + - tripleo-update + vars: + ovn_bridge: br-migration + tags: + - setup + become: false + +# +# Once everything is migrated prepare everything by syncing the neutron DB +# into the OVN NB database, and then switching the dataplane to br-int +# letting ovn-controller take control, afterwards any remaining neutron +# resources, namespaces or processes which are not needed anymore are +# cleaned up. +# + +- name: Do the DB sync and dataplane switch + hosts: ovn-controllers, ovn-dbs + roles: + - migration + vars: + ovn_bridge: br-int + tags: + - migration + +# +# Verify that the initial resources are still reachable, remove them, +# and afterwards create new resources and repeat the connectivity tests. +# + +- name: Post migration + hosts: localhost + roles: + - delete-neutron-resources + - post-migration + tags: + - post-migration + +# +# Final step to make sure tripleo knows about OVNIntegrationBridge == br-int. +# + +- name: Rerun the stack update to reset the OVNIntegrationBridge to br-int + hosts: localhost + roles: + - tripleo-update + vars: + ovn_bridge: br-int + tags: + - setup + become: false + +# +# Final validation after tripleo update to br-int +# + +- name: Final validation + hosts: localhost + vars: + validate_premigration_resources: false + roles: + - post-migration + tags: + - final-validation + + +# +# Announce that it's done and ready. +# + +- hosts: localhost + tasks: + - name: Migration successful. + debug: + msg: Migration from ML2OVS to OVN is now complete. + diff --git a/tools/ovn_migration/tripleo_environment/playbooks/reduce-dhcp-renewal-time.yml b/tools/ovn_migration/tripleo_environment/playbooks/reduce-dhcp-renewal-time.yml new file mode 100644 index 00000000000..1361d8b8699 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/reduce-dhcp-renewal-time.yml @@ -0,0 +1,24 @@ +--- + +- hosts: overcloud-controllers + tasks: + - name: Update dhcp_agent configuration file option 'dhcp_renewal_time' + ini_file: + path=/var/lib/config-data/puppet-generated/neutron/etc/neutron/dhcp_agent.ini + section=DEFAULT + backup=yes + option=dhcp_renewal_time + value={{ renewal_time }} + create=no + ignore_errors: yes + + - block: + - name: Get the neutron dhcp agent docker id + shell: + docker ps | grep neutron_dhcp | awk '{print $1}' + register: dhcp_agent_docker_id + ignore_errors: yes + + - name: Restart neutron dhcp agent + command: docker restart {{ dhcp_agent_docker_id.stdout }} + ignore_errors: yes diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/backup/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/backup/tasks/main.yml new file mode 100644 index 00000000000..5444171d059 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/backup/tasks/main.yml @@ -0,0 +1,19 @@ +# The following tasks ensure that we have backup data which is +# necessary later for cleanup (like l3/dhcp/metadata agent definitions) + +- name: "Ensure the ovn backup directory" + file: path="{{ ovn_migration_backups }}" state=directory + +- name: "Save the tripleo container definitions" + shell: | + # only copy them the first time, otherwise, on a later run when + # it has been already migrated to OVN we would miss the data + if [ ! -d {{ ovn_migration_backups }}/tripleo-config ]; then + cp -rfp /var/lib/tripleo-config {{ ovn_migration_backups }} + echo "Backed up" + fi + register: command_result + changed_when: "'Backed up' in command_result.stdout" + +# TODO(majopela): Include steps for backing up the mysql database on the +# controllers and the undercloud before continuing \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/defaults/main.yml new file mode 100644 index 00000000000..4098766968f --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +ovn_migration_temp_dir_del: "{{ working_dir }}/delete_neutron_resources" \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/tasks/main.yml new file mode 100644 index 00000000000..5e58fa9cbeb --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Delete temp file directory if present + file: + state: absent + path: "{{ ovn_migration_temp_dir_del }}" + +- name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_temp_dir_del }}" + +- name: Generate neutron resources cleanup script + template: + src: "delete-neutron-resources.sh.j2" + dest: "{{ ovn_migration_temp_dir_del }}/delete-neutron-resources.sh" + mode: 0744 + +- name: Deleting the neutron agents + shell: > + {{ ovn_migration_temp_dir_del }}/delete-neutron-resources.sh 2>&1 > + {{ ovn_migration_temp_dir_del }}/delete-neutron-resources.sh.log + changed_when: true diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/templates/delete-neutron-resources.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/templates/delete-neutron-resources.sh.j2 new file mode 100644 index 00000000000..8a33952ab65 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/delete-neutron-resources/templates/delete-neutron-resources.sh.j2 @@ -0,0 +1,29 @@ +#!/bin/bash + +set -x + +source {{ overcloudrc }} + +# Delete non alive neutron agents +for i in `openstack network agent list | grep neutron- | grep -v ':-)' | awk {'print $2'}` +do + openstack network agent delete $i +done + + +delete_network_ports() { + net_id=$1 + for p in `openstack port list --network $net_id | grep -v ID | awk '{print $2}'` + do + openstack port delete $p + done +} + +# Delete HA networks +for i in `openstack network list | grep "HA network tenant" | awk '{print $2}'` +do + delete_network_ports $i + openstack network delete $i +done + +exit 0 diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/defaults/main.yml new file mode 100644 index 00000000000..94af8d4ab72 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/defaults/main.yml @@ -0,0 +1,15 @@ +--- + +agent_cleanups: + neutron_l3_agent: + config: --config-file /usr/share/neutron/neutron-dist.conf --config-dir /usr/share/neutron/l3_agent --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/l3_agent.ini --config-dir /etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-l3-agent --log-file=/var/log/neutron/netns-cleanup-l3.log + cleanup_type: l3 + netns_regex: "fip-|snat-|qrouter-" + + neutron_dhcp: + config: --config-file /usr/share/neutron/neutron-dist.conf --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/dhcp_agent.ini --config-dir /etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-dhcp-agent --log-file=/var/log/neutron/netns-cleanup-dhcp.log + cleanup_type: dhcp + netns_regex: "qdhcp-" + +tunnel_bridge: "br-tun" +ovn_bridge: "br-int" diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/activate-ovn.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/activate-ovn.yml new file mode 100644 index 00000000000..694da73bc23 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/activate-ovn.yml @@ -0,0 +1,15 @@ +--- +- name: Generate OVN activation script + template: + src: "activate-ovn.sh.j2" + dest: "/tmp/activate-ovn.sh" + mode: 0744 + +- name: Run OVN activation script + shell: > + /tmp/activate-ovn.sh 2>&1 > /tmp/activate-ovn.sh.log + +- name: Delete OVN activate script + file: + state: absent + path: /tmp/activate-ovn.sh diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/cleanup-dataplane.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/cleanup-dataplane.yml new file mode 100644 index 00000000000..d9c8661161e --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/cleanup-dataplane.yml @@ -0,0 +1,79 @@ +--- +- name: Quickly disable neutron router and dhcp interfaces + shell: | + for p in `ovs-vsctl show | egrep 'qr-|ha-|qg-|rfp-' | grep Interface | awk '{print $2}'` + do + # p will be having quotes. Eg. "hr-xxxx". So strip the quotes + p=`echo $p | sed -e 's/"//g'` + ovs-vsctl clear Interface $p external-ids + ovs-vsctl set Interface $p admin-state=down + done + + # dhcp tap ports cannot be easily distinguished from ovsfw ports, so we + # list them from within the qdhcp namespaces + + for netns in `ip netns | awk '{ print $1 }' | grep qdhcp-`; do + for dhcp_port in `ip netns exec $netns ip -o link show | awk -F': ' '{print $2}' | grep tap`; do + ovs-vsctl clear Interface $dhcp_port external-ids + ovs-vsctl set Interface $dhcp_port admin-state=down + done + done + + +- name: Clean neutron datapath security groups from iptables + shell: | + iptables-save > /tmp/iptables-before-cleanup + cat /tmp/iptables-before-cleanup | grep -v neutron-openvswi | \ + grep -v neutron-filter > /tmp/iptables-after-cleanup + + if ! cmp /tmp/iptables-before-cleanup /tmp/iptables-after-cleanup + then + cat /tmp/iptables-after-cleanup | iptables-restore + echo "Security groups cleaned" + fi + register: out + changed_when: "'Security groups cleaned' in out.stdout" + +- name: Cleanup neutron datapath resources + shell: | + # avoid cleaning up dhcp namespaces if the neutron dhcp agent is up (SR-IOV use case) + if [[ "{{ item.value.cleanup_type }}" == "dhcp" ]]; then + docker inspect neutron_dhcp && echo "Shouldn't clean DHCP namespaces if neutron_dhcp docker is up" && exit 0 + fi + + if ip netns | egrep -e "{{ item.value.netns_regex }}" + then + echo "Cleaning up" + cmd="$(paunch debug --file {{ ovn_migration_backups }}/tripleo-config/hashed-container-startup-config-step_4.json \ + --action print-cmd --container {{ item.key }} \ + --interactive | \ + sed 's/--interactive /--volume=\/tmp\/cleanup-{{ item.key }}.sh:\/cleanup.sh:ro /g ' )" + + f="/tmp/cleanup-{{ item.key }}.sh" + f_cmd="/tmp/container-cmd-{{ item.key }}.sh" + + echo "#!/bin/sh" > $f + echo "set -x" >> $f + echo "set -e" >> $f + echo "sudo -E kolla_set_configs" >> $f + echo "neutron-netns-cleanup {{ item.value.config }} --agent-type {{ item.value.cleanup_type }} --force" >> $f + + chmod a+x $f + + echo $cmd /cleanup.sh + + echo "#!/bin/sh" > $f_cmd + echo "set -x" >> $f_cmd + echo "set -e" >> $f_cmd + echo $cmd /cleanup.sh >> $f_cmd + chmod a+x $f_cmd + $f_cmd + + fi + with_dict: "{{ agent_cleanups }}" + register: out + changed_when: "'Cleaning up' in out.stdout" + + + + diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/clone-dataplane.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/clone-dataplane.yml new file mode 100644 index 00000000000..88c137189a4 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/clone-dataplane.yml @@ -0,0 +1,15 @@ +# we use this instead of a big shell entry because some versions of +# ansible-playbook choke on our script syntax + yaml parsing +- name: Generate script to clone br-int and provider bridges + template: + src: "clone-br-int.sh.j2" + dest: "/tmp/clone-br-int.sh" + mode: 0744 + +- name: Run clone script for dataplane + shell: /tmp/clone-br-int.sh + +- name: Delete clone script + file: + state: absent + path: /tmp/clone-br-int.sh \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/main.yml new file mode 100644 index 00000000000..f05fad71b34 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- include_tasks: clone-dataplane.yml + +- include_tasks: sync-dbs.yml + when: ovn_central is defined + +- include_tasks: activate-ovn.yml + +- include_tasks: cleanup-dataplane.yml + when: ovn_controller is defined + tags: + - cleanup-dataplane diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/sync-dbs.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/sync-dbs.yml new file mode 100644 index 00000000000..70c1b473897 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/tasks/sync-dbs.yml @@ -0,0 +1,20 @@ +--- +- name: Get the neutron docker ID + shell: + docker ps | grep neutron-server-ovn | awk '{print $1}' + register: neutron_docker_id + +- name: Sync neutron db with OVN db (container) - Run 1 + command: docker exec "{{ neutron_docker_id.stdout }}" + neutron-ovn-db-sync-util --config-file /etc/neutron/neutron.conf + --config-file /etc/neutron/plugins/ml2/ml2_conf.ini + --ovn-neutron_sync_mode repair + +- name: Sync neutron db with OVN db (container) - Run 2 + command: docker exec "{{ neutron_docker_id.stdout }}" + neutron-ovn-db-sync-util --config-file /etc/neutron/neutron.conf + --config-file /etc/neutron/plugins/ml2/ml2_conf.ini + --ovn-neutron_sync_mode repair + +- name: Pause and let ovn-controllers settle before doing the final activation (5 minute) + pause: minutes=5 \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/activate-ovn.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/activate-ovn.sh.j2 new file mode 100644 index 00000000000..273fc654d4f --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/activate-ovn.sh.j2 @@ -0,0 +1,41 @@ +#!/bin/bash + +set -x + +docker stop ovn_controller + +# restore bridge mappings +ovn_orig_bm=$(ovs-vsctl get open . external_ids:ovn-bridge-mappings-back) +ovs-vsctl set open . external_ids:ovn-bridge-mappings="$ovn_orig_bm" +ovs-vsctl remove open . external_ids ovn-bridge-mappings-back + +ovn_bms=$(echo $ovn_orig_bm | sed 's/\"//g' | sed 's/,/ /g') + +# Reset OpenFlow protocol version before ovn-controller takes over +ovs-vsctl set Bridge {{ ovn_bridge }} protocols=[] + +for bm in $ovn_bms; do + parts=($(echo $bm | sed 's/:/ /g')) + bridge=${parts[1]} + ovs-vsctl set-fail-mode $bridge standalone + ovs-vsctl set Bridge $bridge protocols=[] + ovs-vsctl del-controller $bridge +done + +# Delete controller from integration bridge +ovs-vsctl del-controller {{ ovn_bridge }} + +# Activate ovn-controller by configuring integration bridge +ovs-vsctl set open . external_ids:ovn-bridge={{ ovn_bridge }} + +docker start ovn_controller + +# Delete ovs bridges - br-tun and br-migration +ovs-vsctl --if-exists del-br {{ tunnel_bridge }} +ovs-vsctl --if-exists del-br br-migration + +for br in $(ovs-vsctl list-br | egrep 'br-mig-[0-9]+'); do + ovs-vsctl --if-exists del-br $br +done + +exit 0 diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/clone-br-int.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/clone-br-int.sh.j2 new file mode 100644 index 00000000000..c573e5e95b2 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/migration/templates/clone-br-int.sh.j2 @@ -0,0 +1,77 @@ +# The purpose of this script is to make a clone of the br-int content +# into br-migration, and to create fake provider bridges. +# This way, while we synchronize the neutron database into the OVN +# northbound DB, and that translates into southbound content all +# the ovn-controllers around are able to create the SBDB content +# safely, without disrupting the existing neutron ml2/ovs dataplane. + +OVN_MIG_PREFIX=br-mig +OVN_BR_MIGRATION=${OVN_BR_MIGRATION:-br-migration} + +function recreate_bridge_mappings() { + function new_bridge_mappings() { + orig_bms=$1 + + if echo $orig_bms | grep $OVN_MIG_PREFIX; then + echo $orig_bms + return + fi + ovn_bms=$(echo $1 | sed 's/\"//g' | sed 's/,/ /g') + final_bm="" + br_n=0 + + for bm in $ovn_bms; do + parts=($(echo $bm | sed 's/:/ /g')) + physnet=${parts[0]} + bridge="${OVN_MIG_PREFIX}-${br_n}" + mapping="${physnet}:${bridge}" + if [[ -z "$final_bm" ]]; then + final_bm=$mapping + else + final_bm="${final_bm},${mapping}" + fi + # ensure bridge + ovs-vsctl --may-exist add-br $bridge + br_n=$(( br_n + 1 )) + done + echo $final_bm + } + ovn_orig_bm=$(ovs-vsctl get open . external_ids:ovn-bridge-mappings) + + # backup the original mapping if we didn't already do + ovs-vsctl get open . external_ids:ovn-bridge-mappings-back || \ + ovs-vsctl set open . external_ids:ovn-bridge-mappings-back="$ovn_orig_bm" + + new_mapping=$(new_bridge_mappings $ovn_orig_bm) + + ovs-vsctl set open . external_ids:ovn-bridge-mappings="$new_mapping" +} + +function copy_interfaces_to_br_migration() { + + interfaces=$(ovs-vsctl list-ifaces br-int | egrep -v 'qr-|ha-|qg-|rfp-') + for interface in $interfaces; do + if [[ "$interface" == "br-int" ]]; then + continue + fi + ifmac=$(ovs-vsctl get Interface $interface external-ids:attached-mac) + if [ $? -ne 0 ]; then + echo "Can't get port details for $interface" + continue + fi + ifstatus=$(ovs-vsctl get Interface $interface external-ids:iface-status) + ifid=$(ovs-vsctl get Interface $interface external-ids:iface-id) + ifname=x$interface + ovs-vsctl -- --may-exist add-port $OVN_BR_MIGRATION $ifname \ + -- set Interface $ifname type=internal \ + -- set Interface $ifname external-ids:iface-status=$ifstatus \ + -- set Interface $ifname external-ids:attached-mac=$ifmac \ + -- set Interface $ifname external-ids:iface-id=$ifid + + echo cloned port $interface from br-int as $ifname on $OVN_BR_MIGRATION + done +} + +recreate_bridge_mappings +docker restart ovn_controller +copy_interfaces_to_br_migration diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/defaults/main.yml new file mode 100644 index 00000000000..e846ad3e831 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/defaults/main.yml @@ -0,0 +1,4 @@ +--- + +ovn_migration_temp_dir: "{{ working_dir }}/post_migration_resources" +validate_premigration_resources: true \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/tasks/main.yml new file mode 100644 index 00000000000..d0d63262db3 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/post-migration/tasks/main.yml @@ -0,0 +1,59 @@ +--- + +# +# Validate pre-migration resources and then clean those up +# + +- name: Validate pre migration resources after migration + include_role: + name: resources/validate + vars: + restart_server: true + when: + - validate_migration|bool + - validate_premigration_resources + +- name: Delete the pre migration resources + include_role: + name: resources/cleanup + tags: + - post-migration + when: + - validate_migration|bool + - validate_premigration_resources + +# +# Create post-migration resources, validate, and then clean up +# + +# Delete any existing resources to make sure we don't conflict on a second run +- name: Delete any post migration resources (preventive) + include_role: + name: resources/cleanup + vars: + resource_suffix: "post" + silent_cleanup: true + when: validate_migration|bool + +- name: Create post-migration resources + include_role: + name: resources/create + vars: + resource_suffix: "post" + when: validate_migration|bool + +- name: Validate post migration resources + include_role: + name: resources/validate + vars: + resource_suffix: "post" + when: validate_migration|bool + +- name: Delete the post migration resources + include_role: + name: resources/cleanup + tags: + - post-migration + vars: + resource_suffix: "post" + when: validate_migration|bool \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/pre-migration/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/pre-migration/tasks/main.yml new file mode 100644 index 00000000000..e3ce8e53997 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/pre-migration/tasks/main.yml @@ -0,0 +1,17 @@ +# Delete any existing resources to make sure we don't conflict on a second run +- name: Delete any existing pre migration resources (preventive) + include_role: + name: resources/cleanup + vars: + silent_cleanup: true + when: validate_migration|bool + +- name: Create the pre migration resource stack + include_role: + name: resources/create + when: validate_migration|bool + +- name: Validate the pre migration resources + include_role: + name: resources/validate + when: validate_migration|bool \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/defaults/main.yml new file mode 100644 index 00000000000..32394d8210b --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/defaults/main.yml @@ -0,0 +1,6 @@ +--- + +cleanup_resource_script: cleanup-resources.sh.j2 +resource_suffix: "pre" +ovn_migration_temp_dir: "{{ working_dir }}/{{ resource_suffix }}_migration_resources" +silent_cleanup: false diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/tasks/main.yml new file mode 100644 index 00000000000..67edbb36d3f --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/tasks/main.yml @@ -0,0 +1,26 @@ +--- + +- name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_temp_dir }}" + +- name: Generate cleanup script + template: + src: "{{ cleanup_resource_script }}" + dest: "{{ ovn_migration_temp_dir }}/cleanup-resources.sh" + mode: 0744 + +- name: Cleaning up the migration resources (verbose) + shell: > + set -o pipefail && + {{ ovn_migration_temp_dir }}/cleanup-resources.sh 2>&1 | tee + {{ ovn_migration_temp_dir }}/cleanup-resources.sh.log + + when: not silent_cleanup + +- name: Cleaning up the migration resources (silent) + shell: > + {{ ovn_migration_temp_dir }}/cleanup-resources.sh >/dev/null 2>&1 + when: silent_cleanup + diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/templates/cleanup-resources.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/templates/cleanup-resources.sh.j2 new file mode 100644 index 00000000000..c2fbe0eaf48 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/cleanup/templates/cleanup-resources.sh.j2 @@ -0,0 +1,32 @@ +#!/bin/bash + +set -x + +source {{ overcloudrc }} + +openstack server delete ovn-migration-server-{{ resource_suffix }} + +openstack port delete ovn-migration-server-port-{{ resource_suffix }} + +server_ip=`cat {{ ovn_migration_temp_dir }}/server_public_ip` + +openstack floating ip delete $server_ip + +openstack router remove subnet ovn-migration-router-{{ resource_suffix }} ovn-migration-subnet-{{ resource_suffix }} + +openstack router unset --external-gateway ovn-migration-router-{{ resource_suffix }} + +openstack router delete ovn-migration-router-{{ resource_suffix }} + +openstack network delete ovn-migration-net-{{ resource_suffix }} + +openstack security group delete ovn-migration-sg-{{ resource_suffix }} + +openstack flavor delete ovn-migration-{{ resource_suffix }} + +openstack image delete cirros-ovn-migration-{{ resource_suffix }} + +openstack keypair delete ovn-migration-{{ resource_suffix }} + +echo "Resource cleanup done" +exit 0 diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/defaults/main.yml new file mode 100644 index 00000000000..401f8c78360 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/defaults/main.yml @@ -0,0 +1,5 @@ +--- + +create_migration_resource_script: create-resources.sh.j2 +resource_suffix: "pre" +ovn_migration_temp_dir: "{{ working_dir }}/{{ resource_suffix }}_migration_resources" diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/tasks/main.yml new file mode 100644 index 00000000000..b30c812187f --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Delete temp file directory if present + file: + state: absent + path: "{{ ovn_migration_temp_dir }}" + +- name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_temp_dir }}" + +- name: Generate resource creation script + template: + src: "{{ create_migration_resource_script }}" + dest: "{{ ovn_migration_temp_dir }}/create-migration-resources.sh" + mode: 0744 + +- name: Creating migration resources + shell: > + set -o pipefail && + {{ ovn_migration_temp_dir }}/create-migration-resources.sh 2>&1 | tee + {{ ovn_migration_temp_dir }}/create-migration-resources.sh.log diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/templates/create-resources.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/templates/create-resources.sh.j2 new file mode 100644 index 00000000000..c9672974e4e --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/create/templates/create-resources.sh.j2 @@ -0,0 +1,128 @@ +#!/bin/bash + +set -x + +source {{ overcloudrc }} + +image_name={{ image_name }} +openstack image show $image_name +if [ "$?" != "0" ] +then + if [ ! -f cirros-0.4.0-x86_64-disk.img ] + then + curl -Lo cirros-0.4.0-x86_64-disk.img https://github.com/cirros-dev/cirros/releases/download/0.4.0/cirros-0.4.0-x86_64-disk.img + fi + + openstack image create "cirros-ovn-migration-{{ resource_suffix }}" --file cirros-0.4.0-x86_64-disk.img \ +--disk-format qcow2 --container-format bare --public + image_name="cirros-ovn-migration-{{ resource_suffix }}" +fi + + +openstack flavor create ovn-migration-{{ resource_suffix }} --ram 1024 --disk 1 --vcpus 1 + +openstack keypair create ovn-migration-{{ resource_suffix }} --private-key {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key + +openstack security group create ovn-migration-sg-{{ resource_suffix }} + +openstack security group rule create --ingress --protocol icmp ovn-migration-sg-{{ resource_suffix }} + +openstack security group rule create --ingress --protocol tcp --dst-port 22 ovn-migration-sg-{{ resource_suffix }} + +openstack network create ovn-migration-net-{{ resource_suffix }} + +neutron net-update ovn-migration-net-{{ resource_suffix }} --mtu 1442 + +openstack subnet create --network ovn-migration-net-{{ resource_suffix }} --subnet-range 172.168.199.0/24 ovn-migration-subnet-{{ resource_suffix }} + +openstack port create --network ovn-migration-net-{{ resource_suffix }} --security-group ovn-migration-sg-{{ resource_suffix }} ovn-migration-server-port-{{ resource_suffix }} + +openstack server create --flavor ovn-migration-{{ resource_suffix }} --image $image_name \ +--key-name ovn-migration-{{ resource_suffix }} \ +--nic port-id=ovn-migration-server-port-{{ resource_suffix }} ovn-migration-server-{{ resource_suffix }} + +openstack router create ovn-migration-router-{{ resource_suffix }} + +openstack router set --external-gateway {{ public_network_name }} ovn-migration-router-{{ resource_suffix }} + +openstack router add subnet ovn-migration-router-{{ resource_suffix }} ovn-migration-subnet-{{ resource_suffix }} + +server_ip=`openstack floating ip create --port ovn-migration-server-port-{{ resource_suffix }} \ +{{ public_network_name }} -c floating_ip_address | grep floating_ip_address \ +| awk '{print $4'}` + +echo $server_ip > {{ ovn_migration_temp_dir }}/server_public_ip + +chmod 0600 {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key + +# Wait till the port is ACTIVE + +echo "Wait till the port is ACTIVE" +port_status=`openstack port show ovn-migration-server-port-{{ resource_suffix }} -c status | grep status | awk '{print $4}'` + +num_attempts=0 +while [ "$port_status" != "ACTIVE" ] +do + num_attempts=$((num_attempts+1)) + sleep 5 + port_status=`openstack port show ovn-migration-server-port-{{ resource_suffix }} -c status | grep status | awk '{print $4}'` + echo "Port status = $port_status" + + if [ $num_attempts -gt 24 ] + then + echo "Port is not up even after 2 minutes. Something is wrong" + exit 1 + fi +done + +echo "VM is up and the port is ACTIVE" + +# Wait till the VM allows ssh connections + +vm_status="down" +num_attempts=0 +while [ "$vm_status" != "up" ] +do + num_attempts=$((num_attempts+1)) + sleep 5 + openstack console log show ovn-migration-server-{{ resource_suffix }} | grep "login:" + if [ "$?" == "0" ] + then + vm_status="up" + else + if [ $num_attempts -gt 60 ] + then + echo "Port is not up even after 5 minutes. Something is wrong." + # Even though something seems wrong, lets try and ping. + break + fi + fi +done + +num_attempts=0 +vm_reachable="false" +while [ "$vm_reachable" != "true" ] +do + num_attempts=$((num_attempts+1)) + sleep 1 + ping -c 3 $server_ip + if [ "$?" == "0" ] + then + vm_reachable="true" + else + if [ $num_attempts -gt 60 ] + then + echo "VM is not reachable. Something is wrong." + # Even though something seems wrong, lets try and ping. + exit 1 + fi + fi +done + +ssh -i {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key -o StrictHostKeyChecking=no \ +-o UserKnownHostsFile=/dev/null cirros@$server_ip date + +rc=$? + +echo "Done with the resource creation : exiting with $rc" +exit $rc diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/defaults/main.yml new file mode 100644 index 00000000000..549535e44ff --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/defaults/main.yml @@ -0,0 +1,5 @@ +validate_resources_script: validate-resources.sh.j2 +server_user_name: "cirros" +restart_server: false +resource_suffix: "pre" +ovn_migration_temp_dir: "{{ working_dir }}/{{ resource_suffix }}_migration_resources" \ No newline at end of file diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/tasks/main.yml new file mode 100644 index 00000000000..4d178dc0039 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/tasks/main.yml @@ -0,0 +1,12 @@ +- name: Generate resource validation script + template: + src: "{{ validate_resources_script }}" + dest: "{{ ovn_migration_temp_dir }}/validate-resources.sh" + mode: 0744 + +- name: Run the validation script + shell: > + set -o pipefail && + {{ ovn_migration_temp_dir }}/validate-resources.sh 2>&1 | tee + {{ ovn_migration_temp_dir }}/validate-resources.sh.log + diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/templates/validate-resources.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/templates/validate-resources.sh.j2 new file mode 100644 index 00000000000..006c5dc5d14 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/resources/validate/templates/validate-resources.sh.j2 @@ -0,0 +1,19 @@ +#!/bin/bash + +set -x +set -e + +source {{ overcloudrc }} + +# This script validates the resources create by the resources/create role. +# It pings to the floating ip of the server and ssh into the server. + +server_ip=`cat {{ ovn_migration_temp_dir }}/server_public_ip` + +echo "Running ping test with -c 3 to the server ip - $server_ip" +ping -c 3 $server_ip + +ssh -i {{ ovn_migration_temp_dir }}/ovn_migration_ssh_key -o StrictHostKeyChecking=no \ +-o UserKnownHostsFile=/dev/null cirros@$server_ip date + +echo "Done with the validation" diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/defaults/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/defaults/main.yml new file mode 100644 index 00000000000..3b256df4e76 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/defaults/main.yml @@ -0,0 +1,4 @@ +--- + +generate_ovn_extras: generate-ovn-extras.sh.j2 +ovn_migration_temp_dir: "{{ working_dir }}/temp_files" diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/tasks/main.yml b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/tasks/main.yml new file mode 100644 index 00000000000..8f1a229f6ba --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/tasks/main.yml @@ -0,0 +1,24 @@ +--- + +- name : Create temp file directory if not present + file: + state: directory + path: "{{ ovn_migration_temp_dir }}" + +- name: Create ovn-extras generation script + template: + src: "{{ generate_ovn_extras }}" + dest: "{{ ovn_migration_temp_dir }}/generate-ovn-extras.sh" + mode: 0755 + +- name: Generate ovn-extras environment file + shell: > + set -o pipefail && + {{ ovn_migration_temp_dir }}/generate-ovn-extras.sh + changed_when: False + +- name: Updating the overcloud stack with OVN services + shell: > + set -o pipefail && + {{ overcloud_ovn_deploy_script }} 2>&1 > {{ overcloud_ovn_deploy_script }}.log + changed_when: true diff --git a/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/templates/generate-ovn-extras.sh.j2 b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/templates/generate-ovn-extras.sh.j2 new file mode 100644 index 00000000000..f5907f12758 --- /dev/null +++ b/tools/ovn_migration/tripleo_environment/playbooks/roles/tripleo-update/templates/generate-ovn-extras.sh.j2 @@ -0,0 +1,7 @@ +#!/bin/bash +set -x + +cat > $HOME/ovn-extras.yaml << EOF +parameter_defaults: + OVNIntegrationBridge: "{{ ovn_bridge }}" +EOF