OpenStack to OpenStack Migration - Security Migration


You need to apply this patch manually for the code shared below to work seamlessly, including versions 1.4.0 and 1.5.0 of openstacksdk.

Lets create our script named "4_security_migration.py," alongside our previous script, we will start by performing the imports in the "cloud_connection.py", just as we did before. Our filtering process will be similar to user and project migration. We will not migrate security rules specific to certain projects (such as admin or service), and among the remaining rules, we will select only those we want to migrate.

For the security group migration, it's important that the projects to which the security groups belong have been migrated in the previous steps. Additionally, it is not recommended to add rules to the default security group, but cloud users may have done so. Therefore, when performing the migration, we will need to consider the content of the default group as well.

Let's first use the CLI to examine the security groups we have, and then we'll write our script's filter accordingly.

erdem@EWUL:~$ openstack --os-cloud oldstack security group list
+--------------------------------------+-------------+------------------------+----------------------------------+------+
| ID                                   | Name        | Description            | Project                          | Tags |
+--------------------------------------+-------------+------------------------+----------------------------------+------+
| 46327701-451f-4a02-9ffa-a3e24767eb8b | default     | Default security group | 0bee73b64d674d2c8550acc691e8bee9 | []   |
| 4b0efc0f-b639-48bd-a517-f469768de2ab | web_server  | HTTP, HTTPS            | 5b656fc728104f97a0d6b8c1e0ac8ed2 | []   |
| 682fc653-92a5-4b78-8a47-cce54ac8887c | local_group | Allow Local Talk       | 5b656fc728104f97a0d6b8c1e0ac8ed2 | []   |
| 6cd31e06-e954-4785-bc75-abd7bafdf888 | ssh_group   | Ssh  - Allowed IPs     | 5b656fc728104f97a0d6b8c1e0ac8ed2 | []   |
| 77d51752-6ff0-48b7-bb58-f4b2c458a450 | default     | Default security group | 5b656fc728104f97a0d6b8c1e0ac8ed2 | []   |
| 83c34689-5454-4b12-8c60-44e4e72bcdbe | default     | Default security group | 7525ce2c81a24e87a202dae3c99f3b56 | []   |
+--------------------------------------+-------------+------------------------+----------------------------------+------+
# --------------------------------------------------------------------------------- #
#   SECURITY TO / NOT TO MIGRATE
#   If migrate_security_of_projects is empty, script migrates every 
#   (excluding filter_security_of_projects) security group including rules. 
#   If project ID/IDs (oldstack) given, then only the security 
#   group and rules belonging to that project is migrated.
migrate_security_of_projects = [
]

filter_security_of_projects = [
    '7525ce2c81a24e87a202dae3c99f3b56', #'oldstack admin project ID'
    '0bee73b64d674d2c8550acc691e8bee9'  #'oldstack service project ID'
]
#   SECURITY TO / NOT TO MIGRATE
# --------------------------------------------------------------------------------- #

The group with the ID "46327701-451f-4a02-9ffa-a3e24767eb8b" belongs to the service project, and "83c34689-5454-4b12-8c60-44e4e72bcdbe" belongs to the admin project, both of which are already excluded from our filter as specified. Everything that remains belongs to the "deneme_proj" project, and their contents need to be migrated and associated. We will perform this operation in the following steps.

  1. We will find the same project that owns the security group on the oldstack in the newstack.
  2. We will retrieve details of the security group on the oldstack.
  3. Using these details, we will create the security group on the newstack. An important point to note is that when each project is created, it comes with a non-deletable group named "default." Therefore, we will not create a group called "default" but will use the existing one as is.
  4. We will iterate through the security group rules on the oldstack and create the same rules in the newly created security group on the newstack. An important point to note is that to avoid mapping rules within the default group, we will clear each rule and recreate them.

Some security group rules may reference other security groups. If a security group rule contains a value for "remote_group_id," and this value does not exist on the target OpenStack, the script will encounter an error. In such a case, you can store the rules that own "remote_group_id" in a list and make them ready for creation after all other operations are completed.

Each part provided in this list is indicated within the code below with its respective number as a comment in the format # [X] #.

for sec_group in connold.network.security_groups():

    sec_group = sec_group.to_dict()
    should_filter = False

    # Check If Security Should NOT Migrate
    if sec_group['tenant_id'] in filter_security_of_projects:
        should_filter = True
    if migrate_security_of_projects:
        if sec_group['tenant_id'] not in migrate_security_of_projects:
            should_filter = True

    # Continue Loop
    if should_filter:
        continue

    # [1] # Determine Project ID on Newstack for New Security Group
    old_project = connold.identity.get_project(sec_group['tenant_id'])
    new_project = connnew.identity.find_project(old_project['name'])

    # [2] # Create New Security Group Details
    c_security_group = {
        "description":  sec_group["description"],
        "name":         sec_group["name"],
        "tenant_id":    new_project['id']
    }

    # [3] # Create New Security Group on Newstack or Find default Group
    new_sec_group = ""
    if c_security_group['name'] != "default":
        new_sec_group = connnew.network.create_security_group(**c_security_group)
    else:
        new_sec_group = connnew.network.find_security_group("default",**c_security_group)

    new_sec_group = new_sec_group.to_dict()

    print("-----------------NEW Sec Group DETAILS----------------------")
    pp.pprint(new_sec_group.to_dict())
    print("---------------------------------------------------------")

    # [4] # Remove Auto Created Default Rules in Newly Created Security Groups
    for rule in new_sec_group['security_group_rules']:
        connnew.network.delete_security_group_rule(rule['id'])

    # [5] # Create Rule Details and Rule
    for rule in sec_group['security_group_rules']:

        c_rule = {
            "description":              rule["description"],
            "direction":                rule["direction"],
            "ethertype":                rule["ethertype"],
            "port_range_max":           rule["port_range_max"],
            "port_range_min":           rule["port_range_min"],
            "protocol":                 rule["protocol"],
            "remote_address_group_id":  rule["remote_address_group_id"],
            "remote_group_id":          rule["remote_group_id"],
            "remote_ip_prefix":         rule["remote_ip_prefix"],
            "security_group_id":        new_sec_group['id']
        }

        # Clean None Values
        for key, value in c_rule.copy().items():
            if value is None:
                del c_rule[key]

        # Get Newstack Remote Group ID
        if 'remote_group_id' in c_rule:
            old_temp_sec_group = connold.network.get_security_group(c_rule['remote_group_id'])
            remote_security_group = {
                "description":  old_temp_sec_group["description"],
                "name":         old_temp_sec_group["name"],
                "tenant_id":    new_project['id']
            }

            new_temp_sec_group = connnew.network.find_security_group(remote_security_group['name'],**c_security_group)
            new_temp_sec_group = new_temp_sec_group.to_dict()
            c_rule['remote_group_id'] = new_temp_sec_group['id']

        new_rule = connnew.network.create_security_group_rule(**c_rule)

        print("-----------------")
        print("Below Rule Added to Project: ", new_project['name'])
        pp.pprint(new_rule.to_dict())
        print("-----------------")

Let's go ahead and run our script and take a look at a small excerpt from the output.

-----------------NEW Sec Group DETAILS----------------------
{   'created_at': '2023-09-04T20:49:26Z',
    'description': 'HTTP, HTTPS',
    'id': '39146166-2c85-494b-a4dd-0710cf68fbcc',
    'location': Munch({'cloud': 'newstack', 'region_name': 'RegionOne', 'zone': None, 'project': Munch({'id': '28683b546d9b4a2e9943810d71ec99cf', 'name': None, 'domain_id': None, 'domain_name': None})}),
    'name': 'web_server',
    'project_id': '28683b546d9b4a2e9943810d71ec99cf',
    'revision_number': 1,
    'security_group_rules': [   {   'created_at': '2023-09-04T20:49:26Z',
                                    'description': None,
                                    'direction': 'egress',
                                    'ethertype': 'IPv6',
                                    'id': '4dff61bd-c1e6-4427-87a5-d043d353443c',
                                    'normalized_cidr': None,
                                    'port_range_max': None,
                                    'port_range_min': None,
                                    'project_id': '28683b546d9b4a2e9943810d71ec99cf',
                                    'protocol': None,
                                    'remote_address_group_id': None,
                                    'remote_group_id': None,
                                    'remote_ip_prefix': None,
                                    'revision_number': 0,
                                    'security_group_id': '39146166-2c85-494b-a4dd-0710cf68fbcc',
                                    'standard_attr_id': 311,
                                    'tags': [],
                                    'tenant_id': '28683b546d9b4a2e9943810d71ec99cf',
                                    'updated_at': '2023-09-04T20:49:26Z'},
                                {   'created_at': '2023-09-04T20:49:26Z',
                                    'description': None,
                                    'direction': 'egress',
                                    'ethertype': 'IPv4',
                                    'id': 'd288967e-38aa-4bfb-a25c-704a64295d45',
                                    'normalized_cidr': None,
                                    'port_range_max': None,
                                    'port_range_min': None,
                                    'project_id': '28683b546d9b4a2e9943810d71ec99cf',
                                    'protocol': None,
                                    'remote_address_group_id': None,
                                    'remote_group_id': None,
                                    'remote_ip_prefix': None,
                                    'revision_number': 0,
                                    'security_group_id': '39146166-2c85-494b-a4dd-0710cf68fbcc',
                                    'standard_attr_id': 310,
                                    'tags': [],
                                    'tenant_id': '28683b546d9b4a2e9943810d71ec99cf',
                                    'updated_at': '2023-09-04T20:49:26Z'}],
    'shared': False,
    'stateful': True,
    'tags': [],
    'tenant_id': '28683b546d9b4a2e9943810d71ec99cf',
    'updated_at': '2023-09-04T20:49:26Z'}
---------------------------------------------------------
-----------------
Below Rule Added to Project:  deneme_proj  Sec Group:  web_server
{   'created_at': '2023-09-04T20:49:26Z',
    'description': '',
    'direction': 'ingress',
    'ether_type': 'IPv4',
    'id': 'eee844df-990a-4f1f-8954-47378aa8a677',
    'location': Munch({'cloud': 'newstack', 'region_name': 'RegionOne', 'zone': None, 'project': Munch({'id': '0a466901b0e14283af568085d07617ca', 'name': 'admin', 'domain_id': None, 'domain_name': 'Default'})}),
    'name': None,
    'normalized_cidr': '0.0.0.0/0',
    'port_range_max': 80,
    'port_range_min': 80,
    'project_id': '0a466901b0e14283af568085d07617ca',
    'protocol': 'tcp',
    'remote_address_group_id': None,
    'remote_group_id': None,
    'remote_ip_prefix': '0.0.0.0/0',
    'revision_number': 0,
    'security_group_id': '39146166-2c85-494b-a4dd-0710cf68fbcc',
    'tags': [],
    'tenant_id': '0a466901b0e14283af568085d07617ca',
    'updated_at': '2023-09-04T20:49:26Z'}
----------------

When we check through the CLI, displaying the entire output may take up a lot of space. To control this, let's print it to the screen with line count for easier verification.

erdem@EWUL:~$ openstack --os-cloud oldstack security group list | wc -l
10
erdem@EWUL:~$ openstack --os-cloud newstack security group list | wc -l
10
erdem@EWUL:~$ openstack --os-cloud oldstack security group rule list | wc -l
27
erdem@EWUL:~$ openstack --os-cloud newstack security group rule list | wc -l
27

Our checks indicate that the total number of security groups and their associated rules match. With this step completed, we can move to Network Migration phase and migrate routers, networks, and floating IPs.

OpenStack to OpenStack Migration:

  1. Article Series
  2. Deployment Info
  3. Preparation
  4. User Migration
  5. Project Migration
  6. Flavor Migration
  7. Security Migration
  8. Network Migration
  9. TBC

References:

Thanks:

Main Photo: John Cameron on Unsplash

Previous