Skip to content

Commit

Permalink
Merge pull request os-migrate#298 from jistr/f/neutron-port-info
Browse files Browse the repository at this point in the history
Take info about server ports from Neutron
  • Loading branch information
jistr authored Nov 12, 2020
2 parents 80162a6 + 82f478f commit f0d364e
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 74 deletions.
1 change: 1 addition & 0 deletions os_migrate/plugins/module_utils/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RES_TYPE_SECURITYGROUPRULE = 'openstack.network.SecurityGroupRule'
RES_TYPE_SECURITYGROUP = 'openstack.network.SecurityGroup'
RES_TYPE_SERVER = 'openstack.compute.Server'
RES_TYPE_SERVER_PORT = 'openstack.network.ServerPort'
RES_TYPE_SUBNET = 'openstack.subnet.Subnet'
RES_TYPE_USER = 'openstack.user.User'
RES_TYPE_KEYPAIR = 'openstack.compute.Keypair'
Expand Down
34 changes: 22 additions & 12 deletions os_migrate/plugins/module_utils/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
from ansible_collections.os_migrate.os_migrate.plugins.module_utils import const


class CannotConverge(Exception):
"""State is incorrect and cannot be auto-converged.
Operator intervention is required.
"""

def __init__(self, message):
super().__init__(message)


class DataVersionMismatch(Exception):
"""Data version does not match OS-Migrate version."""

Expand All @@ -14,6 +23,16 @@ def __init__(self, got_version):
super().__init__(message)


class InconsistentState(Exception):
"""Inconsistent state in data."""

msg_format = "Inconsistent state: '{}'."

def __init__(self, msg):
message = self.msg_format.format(msg)
super().__init__(message)


class UnexpectedResourceType(Exception):
"""Unexpected resource type."""

Expand All @@ -24,15 +43,6 @@ def __init__(self, expected_type, got_type):
super().__init__(message)


class CannotConverge(Exception):
"""State is incorrect and cannot be auto-converged.
Operator intervention is required.
"""

def __init__(self, message):
super().__init__(message)


class UnexpectedValue(Exception):
"""Unexpected value of a variable."""

Expand All @@ -43,10 +53,10 @@ def __init__(self, var, expected, got):
super().__init__(message)


class InconsistentState(Exception):
"""Inconsistent state in data."""
class Unsupported(Exception):
"""Unsupported action."""

msg_format = "Inconsistent state: '{}'."
msg_format = "Unsupported: '{}'."

def __init__(self, msg):
message = self.msg_format.format(msg)
Expand Down
67 changes: 28 additions & 39 deletions os_migrate/plugins/module_utils/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from ansible_collections.os_migrate.os_migrate.plugins.module_utils \
import const, exc, reference, resource
from ansible_collections.os_migrate.os_migrate.plugins.module_utils.server_port \
import server_ports, ServerPort


class Server(resource.Resource):
Expand Down Expand Up @@ -39,7 +41,7 @@ class Server(resource.Resource):
'tags',
]
params_from_refs = [
'addresses_refs',
'ports',
'flavor_ref',
'image_ref',
'security_group_refs',
Expand Down Expand Up @@ -72,6 +74,11 @@ def from_sdk(cls, conn, sdk_resource):

def create(self, conn, block_device_mapping):
sdk_params = self.sdk_params(conn)

# Simple port creation via Nova. Subsequently we may add
# support for advanced port creation via Neutron.
self.update_sdk_params_networks_simple(conn, sdk_params)

self.update_sdk_params_block_device_mapping(sdk_params, block_device_mapping)
return conn.compute.create_server(**sdk_params)

Expand All @@ -85,16 +92,6 @@ def sdk_params(self, conn):
refs['security_group_refs'],
))

sdk_params['networks'] = []
addresses = refs['addresses_ids']
for net_id, net_ports in addresses.items():
for port in net_ports:
if port['OS-EXT-IPS:type'] == 'fixed':
sdk_params['networks'].append({
"uuid": net_id,
"fixed_ip": port['addr'],
})

return sdk_params

def update_sdk_params_block_device_mapping(self, sdk_params, block_device_mapping):
Expand Down Expand Up @@ -138,6 +135,19 @@ def update_sdk_params_block_device_mapping(self, sdk_params, block_device_mappin
else:
sdk_params.pop('image_id')

def update_sdk_params_networks_simple(self, conn, sdk_params):
sdk_params['networks'] = []
ports = list(map(ServerPort.from_data, self.params()['ports']))
for port in ports:
try:
sdk_params['networks'].append(port.nova_sdk_params(conn))
except exc.InconsistentState as e:
params, info = self.params_and_info()
raise exc.InconsistentState(
"Error creating network parameters for server '{0}' ({1}): {2}"
.format(params['name'], info['id'], e)
) from e

@staticmethod
def _find_sdk_res(conn, name_or_id, filters=None):
return conn.compute.find_server(name_or_id, **(filters or {}))
Expand All @@ -146,20 +156,6 @@ def _find_sdk_res(conn, name_or_id, filters=None):
def _refs_from_sdk(conn, sdk_res):
refs = {}

# Nova only returns network names in response, not IDs. This
# can be insufficient in edge cases. We have to hope that
# private/public network names don't collide, and do a lookup
# without project scope.
refs['addresses_refs'] = sdk_res['addresses']
refs['addresses_ids'] = {}
for net_name, net_addrs in sdk_res['addresses'].items():
net_id = reference.network_id(conn, {
'name': net_name,
'project_name': None,
'domain_name': None,
})
refs['addresses_ids'][net_id] = net_addrs

# There are multiple representations of server in SDK, some of
# them don't have flavor ID info.
flavor_id = sdk_res['flavor'].get('id')
Expand All @@ -182,26 +178,17 @@ def _refs_from_sdk(conn, sdk_res):
refs['security_group_refs'] = [
reference.security_group_ref(conn, sec_group['id'])
for sec_group in sec_groups]

sdk_ports = server_ports(conn, sdk_res)
ser_ports = map(lambda p: ServerPort.from_sdk(conn, p), sdk_ports)
refs['ports'] = list(map(lambda p: p.data, ser_ports))

return refs

def _refs_from_ser(self, conn, filters=None):
refs = {}
params = self.params()

# Nova only returns network names in response, not IDs. This
# can be insufficient in edge cases. We have to hope that
# private/public network names don't collide, and do a lookup
# without project scope.
refs['addresses_refs'] = params['addresses_refs']
refs['addresses_ids'] = {}
for net_name, net_addrs in refs['addresses_refs'].items():
net_id = reference.network_id(conn, {
'name': net_name,
'project_name': None,
'domain_name': None,
})
refs['addresses_ids'][net_id] = net_addrs

refs['flavor_ref'] = params['flavor_ref']
refs['flavor_id'] = reference.flavor_id(
conn, params['flavor_ref'])
Expand All @@ -214,4 +201,6 @@ def _refs_from_ser(self, conn, filters=None):
reference.security_group_id(conn, sec_group_ref)
for sec_group_ref in params['security_group_refs']]

refs['ports'] = params['ports']

return refs
95 changes: 95 additions & 0 deletions os_migrate/plugins/module_utils/server_port.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import openstack

from ansible_collections.os_migrate.os_migrate.plugins.module_utils \
import exc, const, reference, resource


def server_ports(conn, sdk_ser):
# the device_owner part after the colon varies - it is the availability zone
return filter(lambda p: p.get('device_owner', '').startswith('compute:'),
conn.network.ports(device_id=sdk_ser['id']))


class ServerPort(resource.Resource):

resource_type = const.RES_TYPE_SERVER_PORT
sdk_class = openstack.network.v2.port.Port

info_from_sdk = [
'id',
'device_owner',
'device_id',
]
params_from_refs = [
'fixed_ips_refs',
'network_ref',
]
sdk_params_from_params = []
sdk_params_from_refs = [
'fixed_ips',
'network_id',
]

@classmethod
def from_sdk(cls, conn, sdk_resource):
obj = super(ServerPort, cls).from_sdk(conn, sdk_resource)
# the part after the colon varies - it is the availability zone
if not sdk_resource['device_owner'].startswith('compute:'):
raise exc.UnexpectedValue(
'device_owner', 'compute:*', sdk_resource['device_owner'])
return obj

def create_or_update(self, conn, filters=None):
raise exc.Unsupported("Direct ServerPort.create_or_update call is unsupported.")

def nova_sdk_params(self, conn):
refs = self._refs_from_ser(conn)

if len(refs['fixed_ips']) != 1:
message = (
"Using simple port creation via Nova is only supported for a single "
"IP address per port, but on network '{0}' it has these addresses: {1}."
.format(refs['network_ref'].get('name', ''),
refs['fixed_ips_refs'])
)
raise exc.InconsistentState(message)

return {
"uuid": refs['network_id'],
"fixed_ip": refs['fixed_ips'][0]['ip_address'],
}

@staticmethod
def _refs_from_sdk(conn, sdk_res):
refs = {}

refs['fixed_ips'] = sdk_res['fixed_ips']
refs['fixed_ips_refs'] = []
for fixed_ip in sdk_res['fixed_ips']:
refs['fixed_ips_refs'].append({
'ip_address': fixed_ip['ip_address'],
'subnet_ref': reference.subnet_ref(conn, fixed_ip['subnet_id']),
})
refs['network_id'] = sdk_res['network_id']
refs['network_ref'] = reference.network_ref(conn, sdk_res['network_id'])

return refs

def _refs_from_ser(self, conn, filters=None):
refs = {}
params = self.params()

refs['fixed_ips_refs'] = params['fixed_ips_refs']
refs['fixed_ips'] = []
for fixed_ip in params['fixed_ips_refs']:
refs['fixed_ips'].append({
'ip_address': fixed_ip['ip_address'],
'subnet_id': reference.subnet_id(conn, fixed_ip['subnet_ref']),
})
refs['network_ref'] = params['network_ref']
refs['network_id'] = reference.network_id(conn, params['network_ref'])

return refs
63 changes: 40 additions & 23 deletions os_migrate/tests/unit/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,6 @@ def sdk_server():

def server_refs():
return {
'addresses_ids': {
'uuid-external-network': [
{'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:d7:ae:16',
'OS-EXT-IPS:type': 'fixed',
'addr': '10.19.2.50',
'version': 4},
],
},
'addresses_refs': {
'external_network': [
{'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:d7:ae:16',
'OS-EXT-IPS:type': 'fixed',
'addr': '10.19.2.50',
'version': 4},
],
},
'flavor_id': 'uuid-flavor-m1.small',
'flavor_ref': {
'name': 'm1.small',
Expand All @@ -64,6 +48,34 @@ def server_refs():
'project_name': 'test-project',
'domain_name': 'Default',
},
'ports': [
{
'_info': {
'device_id': 'becd9aa6-8934-4086-a21e-3058452d45e6',
'device_owner': 'compute:None',
'id': '18e85b51-c64e-4c8a-bbba-797d8dd7a3b7',
},
'_migration_params': {},
'params': {
'fixed_ips_refs': [
{
'ip_address': '10.19.2.50',
'subnet_ref': {
'domain_name': '%auth%',
'name': 'osm_subnet',
'project_name': '%auth%',
},
},
],
'network_ref': {
'domain_name': '%auth%',
'name': 'osm_net',
'project_name': '%auth%',
},
},
'type': 'openstack.network.ServerPort',
},
],
'security_group_ids': [
'uuid-secgroup-default',
'uuid-secgroup-testing123',
Expand Down Expand Up @@ -98,14 +110,19 @@ def test_serialize_server(self):
params, info = srv.params_and_info()

self.assertEqual(srv.type(), 'openstack.compute.Server')
self.assertEqual(params['addresses_refs'], {
'external_network': [
{'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:d7:ae:16',
'OS-EXT-IPS:type': 'fixed',
'addr': '10.19.2.50',
'version': 4},
self.assertEqual(
params['ports'][0]['params']['fixed_ips_refs'],
[
{
'ip_address': '10.19.2.50',
'subnet_ref': {
'domain_name': '%auth%',
'name': 'osm_subnet',
'project_name': '%auth%',
},
},
],
})
)
self.assertEqual(params['description'], 'test server')
self.assertEqual(params['flavor_ref']['name'], 'm1.small')
self.assertEqual(info['id'], 'uuid-test-server')
Expand Down
Loading

0 comments on commit f0d364e

Please sign in to comment.