iotronic/iotronic/common/neutron.py

246 lines
8.1 KiB
Python

# 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.
from iotronic.common import exception
from iotronic.common.i18n import _
from keystoneauth1 import identity
from keystoneauth1 import session
from neutronclient.common import exceptions as neutron_exceptions
from neutronclient.v2_0 import client as clientv20
from oslo_config import cfg
from oslo_log import log
CONF = cfg.CONF
LOG = log.getLogger(__name__)
neutron_opts = [
cfg.StrOpt('url',
default='http://localhost:9696/',
help=('URL neutron')),
cfg.StrOpt('retries',
default=3,
help=('retries neutron')),
cfg.StrOpt('auth_strategy',
default='noauth',
help=('auth_strategy neutron')),
cfg.StrOpt('username',
default='neutron',
help=('neutron username')),
cfg.StrOpt('password',
default='',
help=('password')),
cfg.StrOpt('project_name',
default='service',
help=('service')),
cfg.StrOpt('project_domain_name',
default='default',
help=('domain id')),
cfg.StrOpt('auth_url',
default='http://localhost:35357',
help=('auth')),
cfg.StrOpt('project_domain_id',
default='default',
help=('project domain id')),
cfg.StrOpt('user_domain_id',
default='default',
help=('user domain id')),
]
CONF.register_opts(neutron_opts, 'neutron')
DEFAULT_NEUTRON_URL = CONF.neutron.url
_NEUTRON_SESSION = None
"""def _get_neutron_session():
global _NEUTRON_SESSION
if not _NEUTRON_SESSION:
_NEUTRON_SESSION = keystone.get_session('neutron')
return _NEUTRON_SESSION
"""
def get_client(token=None):
auth = identity.Password(auth_url=CONF.neutron.auth_url,
username=CONF.neutron.username,
password=CONF.neutron.password,
project_name=CONF.neutron.project_name,
project_domain_id=CONF.neutron.project_domain_id,
user_domain_id=CONF.neutron.user_domain_id)
sess = session.Session(auth=auth)
neutron = clientv20.Client(session=sess)
return neutron
def subnet_info(subnet_uuid):
"""Get subnet details : we need the CIDR
:param subnet_uuid: Neutron subnet UUID.
"""
client = get_client()
try:
info = client.show_subnet(subnet_uuid)
return info
except Exception as e:
LOG.error(str(e))
def unbind_neutron_port(port_id, client=None):
"""Unbind a neutron port
Remove a neutron port's binding profile and host ID so that it returns to
an unbound state.
:param port_id: Neutron port ID.
:param client: Optional a Neutron client object.
:raises: NetworkError
"""
if not client:
client = get_client()
body = {'port': {'binding:host_id': '',
'binding:profile': {}}}
try:
client.update_port(port_id, body)
# NOTE(vsaienko): Ignore if port was deleted before calling vif detach.
except neutron_exceptions.PortNotFoundClient:
LOG.info('Port %s was not found while unbinding.', port_id)
except neutron_exceptions.NeutronClientException as e:
msg = (_('Unable to clear binding profile for '
'neutron port %(port_id)s. Error: '
'%(err)s') % {'port_id': port_id, 'err': e})
LOG.exception(msg)
raise exception.NetworkError(msg)
def update_port_address(port_id, address):
"""Update a port's mac address.
:param port_id: Neutron port id.
:param address: new MAC address.
:raises: FailedToUpdateMacOnPort
"""
client = get_client()
port_req_body = {'port': {'mac_address': address}}
try:
msg = (_("Failed to get the current binding on Neutron "
"port %s.") % port_id)
port = client.show_port(port_id).get('port', {})
binding_host_id = port.get('binding:host_id')
binding_profile = port.get('binding:profile')
if binding_host_id:
# Unbind port before we update it's mac address, because you can't
# change a bound port's mac address.
msg = (_("Failed to remove the current binding from "
"Neutron port %s, while updating its MAC "
"address.") % port_id)
unbind_neutron_port(port_id, client=client)
port_req_body['port']['binding:host_id'] = binding_host_id
port_req_body['port']['binding:profile'] = binding_profile
msg = (_("Failed to update MAC address on Neutron port %s.") % port_id)
client.update_port(port_id, port_req_body)
except (neutron_exceptions.NeutronClientException, exception.NetworkError):
LOG.exception(msg)
raise exception.FailedToUpdateMacOnPort(port_id=port_id)
def _verify_security_groups(security_groups, client):
"""Verify that the security groups exist.
:param security_groups: a list of security group UUIDs; may be None or
empty
:param client: Neutron client
:raises: NetworkError
"""
if not security_groups:
return
try:
neutron_sec_groups = (
client.list_security_groups().get('security_groups', []))
except neutron_exceptions.NeutronClientException as e:
msg = (_("Could not retrieve security groups from neutron: %(exc)s") %
{'exc': e})
LOG.exception(msg)
raise exception.NetworkError(msg)
existing_sec_groups = [sec_group['id'] for sec_group in neutron_sec_groups]
missing_sec_groups = set(security_groups) - set(existing_sec_groups)
if missing_sec_groups:
msg = (_('Could not find these security groups '
'(specified via iotronic '
'config) in neutron: %(ir-sg)s')
% {'ir-sg': list(missing_sec_groups)})
LOG.error(msg)
raise exception.NetworkError(msg)
def add_port_to_network(board, network_uuid, subnet_uuid,
security_groups=None):
client = get_client()
_verify_security_groups(security_groups, client)
LOG.debug('For wagent %(wagent)s, creating neutron port on network '
'%(network_uuid)s.',
{'wagent': board.agent, 'network_uuid': network_uuid})
body = {
'port': {
'network_id': network_uuid,
'project_id': board.project,
'device_id': board.uuid,
'admin_state_up': True,
'device_owner': 'iot:board',
'binding:host_id': board.agent,
'fixed_ips': [{
'subnet_id': subnet_uuid
}]
}
}
if security_groups:
body['port']['security_groups'] = security_groups
try:
port = client.create_port(body)
except neutron_exceptions.NeutronClientException as e:
LOG.warning("Could not create neutron port for wagent's "
"%(wagent)s on the neutron "
"network %(net)s. %(exc)s",
{'net': network_uuid, 'wagent': board.agent,
'exc': e})
else:
return port
def delete_port(wagent, port_uuid):
client = get_client()
LOG.debug('For wagent %(wagent)s, removing neutron port %(port_uuid)s',
{'wagent': wagent, 'port_uuid': port_uuid})
try:
client.delete_port(port_uuid)
return 1
except neutron_exceptions.NeutronClientException as e:
LOG.warning("Could not delete neutron port from wagent's "
"%(wagent)s : %(exc)s ", {'wagent': wagent, 'exc': e})
return 0