2605N/A# vim: tabstop=4 shiftwidth=4 softtabstop=4
2605N/A
6033N/A# Copyright (c) 2014, 2016 Oracle and/or its affiliates. All rights reserved.
2605N/A#
2605N/A# Licensed under the Apache License, Version 2.0 (the "License"); you may
2605N/A# not use this file except in compliance with the License. You may obtain
2605N/A# a copy of the License at
2605N/A#
2605N/A# http://www.apache.org/licenses/LICENSE-2.0
2605N/A#
2605N/A# Unless required by applicable law or agreed to in writing, software
2605N/A# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2605N/A# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2605N/A# License for the specific language governing permissions and limitations
2605N/A# under the License.
2605N/A#
2605N/A# @author: Girish Moodalbail, Oracle, Inc.
2605N/A#
2605N/A
2769N/Aimport netaddr
2769N/A
2900N/Afrom neutron.agent.linux import utils
2605N/A
2605N/A
2605N/Aclass CommandBase(object):
2605N/A @classmethod
2605N/A def execute_with_pfexec(cls, cmd, **kwargs):
2605N/A # uses pfexec
2605N/A cmd.insert(0, '/usr/bin/pfexec')
2605N/A return utils.execute(cmd, **kwargs)
2605N/A
2605N/A @classmethod
2605N/A def execute(cls, cmd, **kwargs):
2605N/A return utils.execute(cmd, **kwargs)
2605N/A
2605N/A
2605N/Aclass IPInterface(CommandBase):
2605N/A '''Wrapper around Solaris ipadm(1m) command.'''
2605N/A
2605N/A def __init__(self, ifname):
2605N/A self._ifname = ifname
2605N/A
2605N/A @classmethod
2605N/A def ifname_exists(cls, ifname):
6033N/A try:
6033N/A cmd = ['/usr/sbin/ipadm', 'show-if', '-po', 'ifname', ifname]
6033N/A cls.execute(cmd, log_fail_as_error=False)
6033N/A except Exception:
6033N/A return False
6033N/A return True
6033N/A
2605N/A @classmethod
2605N/A def ipaddr_exists(cls, ifname, ipaddr):
2605N/A
2605N/A if not cls.ifname_exists(ifname):
2605N/A return False
2605N/A
2605N/A cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'addr', ifname]
2605N/A stdout = cls.execute(cmd)
2605N/A
2605N/A return ipaddr in stdout
2605N/A
2605N/A def ipaddr_list(self, filters=None):
2900N/A cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'type,addr',
2605N/A self._ifname]
2605N/A stdout = self.execute(cmd)
2605N/A atype_addrs = stdout.strip().split('\n')
2605N/A result = {}
2605N/A for atype_addr in atype_addrs:
2900N/A atype, addr = atype_addr.split(':', 1)
2605N/A val = result.get(atype)
2605N/A if val is None:
2605N/A result[atype] = []
2605N/A val = result.get(atype)
2900N/A # in the case of IPv6 addresses remove any escape '\' character
2900N/A val.append(addr.replace("\\", ""))
2605N/A return result
2605N/A
2769N/A def create_address(self, ipaddr, addrobjname=None, temp=True):
2605N/A if not self.ifname_exists(self._ifname):
2605N/A # create ip interface
2605N/A cmd = ['/usr/sbin/ipadm', 'create-ip', self._ifname]
2605N/A if temp:
2605N/A cmd.append('-t')
2605N/A self.execute_with_pfexec(cmd)
6033N/A elif self.ipaddr_exists(self._ifname, ipaddr):
2605N/A return
2605N/A
2769N/A # If an address is IPv6, then to create a static IPv6 address
2769N/A # we need to create link-local address first
2769N/A if netaddr.IPNetwork(ipaddr).version == 6:
2769N/A # check if link-local address already exists
2769N/A cmd = ['/usr/sbin/dladm', 'show-linkprop', '-co', 'value',
2769N/A '-p', 'mac-address', self._ifname]
2769N/A stdout = self.execute(cmd)
2769N/A mac_addr = stdout.splitlines()[0].strip()
2769N/A ll_addr = netaddr.EUI(mac_addr).ipv6_link_local()
2769N/A
2769N/A if not self.ipaddr_exists(self._ifname, str(ll_addr)):
2769N/A # create a link-local address
2769N/A cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'static', '-a',
2769N/A str(ll_addr), self._ifname]
2769N/A if temp:
2769N/A cmd.append('-t')
2769N/A self.execute_with_pfexec(cmd)
2769N/A
2769N/A cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'static', '-a',
2605N/A ipaddr, self._ifname]
2605N/A if temp:
2605N/A cmd.append('-t')
2605N/A
3202N/A self.execute_with_pfexec(cmd)
2605N/A
6033N/A def create_addrconf(self, temp=True):
6033N/A if not self.ifname_exists(self._ifname):
6033N/A # create ip interface
6033N/A cmd = ['/usr/sbin/ipadm', 'create-ip', self._ifname]
6033N/A if temp:
6033N/A cmd.append('-t')
6033N/A self.execute_with_pfexec(cmd)
6033N/A else:
6033N/A cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'type', self._ifname]
6033N/A stdout = self.execute(cmd)
6033N/A if 'addrconf' in stdout:
6033N/A return
6033N/A
6033N/A cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'addrconf',
6033N/A self._ifname]
6033N/A if temp:
6033N/A cmd.append('-t')
6033N/A self.execute_with_pfexec(cmd)
6033N/A
2605N/A def delete_address(self, ipaddr):
2605N/A if not self.ipaddr_exists(self._ifname, ipaddr):
2605N/A return
2605N/A
2605N/A cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'addrobj,addr',
2605N/A self._ifname]
2605N/A stdout = self.execute(cmd)
2605N/A aobj_addrs = stdout.strip().split('\n')
2605N/A for aobj_addr in aobj_addrs:
2605N/A if ipaddr not in aobj_addr:
2605N/A continue
2605N/A aobj = aobj_addr.split(':')[0]
2605N/A cmd = ['/usr/sbin/ipadm', 'delete-addr', aobj]
2605N/A self.execute_with_pfexec(cmd)
2605N/A break
2605N/A
2769N/A isV6 = netaddr.IPNetwork(ipaddr).version == 6
2769N/A if len(aobj_addrs) == 1 or (isV6 and len(aobj_addrs) == 2):
2605N/A # delete the interface as well
2605N/A cmd = ['/usr/sbin/ipadm', 'delete-ip', self._ifname]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A def delete_ip(self):
2605N/A if not self.ifname_exists(self._ifname):
2605N/A return
2605N/A
2605N/A cmd = ['/usr/sbin/ipadm', 'delete-ip', self._ifname]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A
2605N/Aclass Datalink(CommandBase):
2605N/A '''Wrapper around Solaris dladm(1m) command.'''
2605N/A
2605N/A def __init__(self, dlname):
2605N/A self._dlname = dlname
2605N/A
2605N/A @classmethod
2605N/A def datalink_exists(cls, dlname):
6033N/A try:
6033N/A cmd = ['/usr/sbin/dladm', 'show-link', '-po', 'link', dlname]
6033N/A utils.execute(cmd, log_fail_as_error=False)
6033N/A except Exception:
6033N/A return False
6033N/A return True
2605N/A
2605N/A def connect_vnic(self, evsvport, tenantname=None, temp=True):
2605N/A if self.datalink_exists(self._dlname):
2605N/A return
2605N/A
2605N/A cmd = ['/usr/sbin/dladm', 'create-vnic', '-c', evsvport, self._dlname]
2605N/A if temp:
2605N/A cmd.append('-t')
2605N/A if tenantname:
2605N/A cmd.append('-T')
2605N/A cmd.append(tenantname)
2605N/A
3202N/A self.execute_with_pfexec(cmd)
2605N/A
2900N/A def create_vnic(self, lower_link, mac_address=None, vid=None, temp=True):
2605N/A if self.datalink_exists(self._dlname):
2605N/A return
2605N/A
2900N/A if vid:
2900N/A # If the default_tag of lower_link is same as vid, then there
2900N/A # is no need to set vid
2900N/A cmd = ['/usr/sbin/dladm', 'show-linkprop', '-co', 'value',
2900N/A '-p', 'default_tag', lower_link]
2900N/A stdout = utils.execute(cmd)
6033N/A default_tag = stdout.splitlines()[0].strip()
6033N/A if default_tag == vid or (vid == '1' and default_tag == '0'):
2900N/A vid = '0'
2900N/A else:
2900N/A vid = '0'
2605N/A cmd = ['/usr/sbin/dladm', 'create-vnic', '-l', lower_link,
2900N/A '-m', mac_address, '-v', vid, self._dlname]
2605N/A if temp:
2605N/A cmd.append('-t')
2605N/A
3202N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A def delete_vnic(self):
2605N/A if not self.datalink_exists(self._dlname):
2605N/A return
2605N/A
2605N/A cmd = ['/usr/sbin/dladm', 'delete-vnic', self._dlname]
3202N/A self.execute_with_pfexec(cmd)
2605N/A
4070N/A @classmethod
6033N/A def show_link(cls):
6033N/A cmd = ['/usr/sbin/dladm', 'show-link', '-po', 'link']
4070N/A stdout = utils.execute(cmd)
4070N/A
4070N/A return stdout.splitlines()
4070N/A
2605N/A
2900N/Aclass IPpoolCommand(CommandBase):
2605N/A '''Wrapper around Solaris ippool(1m) command'''
2605N/A
2605N/A def __init__(self, pool_name, role='ipf', pool_type='tree'):
2605N/A self._pool_name = pool_name
2605N/A self._role = role
2605N/A self._pool_type = pool_type
2605N/A
2605N/A def pool_exists(self):
2605N/A cmd = ['/usr/sbin/ippool', '-l', '-m', self._pool_name,
2605N/A '-t', self._pool_type]
2605N/A stdout = self.execute_with_pfexec(cmd)
2605N/A return str(self._pool_name) in stdout
2605N/A
2605N/A def pool_split_nodes(self, ip_cidrs):
2605N/A cmd = ['/usr/sbin/ippool', '-l', '-m', self._pool_name,
2605N/A '-t', self._pool_type]
2605N/A stdout = self.execute_with_pfexec(cmd)
2605N/A existing_nodes = []
2605N/A non_existing_nodes = []
2605N/A for ip_cidr in ip_cidrs:
2605N/A if ip_cidr in stdout:
2605N/A existing_nodes.append(ip_cidr)
2605N/A else:
2605N/A non_existing_nodes.append(ip_cidr)
2605N/A return existing_nodes, non_existing_nodes
2605N/A
2605N/A def add_pool_nodes(self, ip_cidrs):
2605N/A ip_cidrs = self.pool_split_nodes(ip_cidrs)[1]
2605N/A
2605N/A for ip_cidr in ip_cidrs:
2605N/A cmd = ['/usr/sbin/ippool', '-a', '-m', self._pool_name,
2605N/A '-i', ip_cidr]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A def remove_pool_nodes(self, ip_cidrs):
2605N/A ip_cidrs = self.pool_split_nodes(ip_cidrs)[0]
2605N/A
2605N/A for ip_cidr in ip_cidrs:
2605N/A cmd = ['/usr/sbin/ippool', '-r', '-m', self._pool_name,
2605N/A '-i', ip_cidr]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A def add_pool(self):
2605N/A if self.pool_exists():
2605N/A return
2605N/A
2605N/A cmd = ['/usr/sbin/ippool', '-A', '-m', self._pool_name,
2605N/A '-o', self._role, '-t', self._pool_type]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A def remove_pool(self):
2605N/A if not self.pool_exists():
2605N/A return
2605N/A
2605N/A # This command will fail if ippool is in use by ipf, so the
2605N/A # caller has to ensure that it's not being used in an ipf rule
2605N/A cmd = ['/usr/sbin/ippool', '-R', '-m', self._pool_name,
2605N/A '-o', self._role, '-t', self._pool_type]
2605N/A self.execute_with_pfexec(cmd)
2605N/A
2605N/A
2900N/Aclass IPfilterCommand(CommandBase):
2605N/A '''Wrapper around Solaris ipf(1m) command'''
2605N/A
2949N/A def _split_rules(self, rules, version):
2605N/A # assumes that rules are inbound!
2605N/A cmd = ['/usr/sbin/ipfstat', '-i']
2949N/A if version == 6:
2949N/A cmd.insert(1, '-6')
2605N/A stdout = self.execute_with_pfexec(cmd)
2605N/A existing_rules = []
2605N/A non_existing_rules = []
2605N/A for rule in rules:
2605N/A if rule in stdout:
2605N/A existing_rules.append(rule)
2605N/A else:
2605N/A non_existing_rules.append(rule)
2605N/A
2605N/A return existing_rules, non_existing_rules
2605N/A
2949N/A def add_rules(self, rules, version=4):
2949N/A rules = self._split_rules(rules, version)[1]
3202N/A if not rules:
3202N/A return
3202N/A process_input = '\n'.join(rules) + '\n'
2605N/A cmd = ['/usr/sbin/ipf', '-f', '-']
2949N/A if version == 6:
2949N/A cmd.insert(1, '-6')
3202N/A self.execute_with_pfexec(cmd, process_input=process_input)
2605N/A
2949N/A def remove_rules(self, rules, version=4):
2949N/A rules = self._split_rules(rules, version)[0]
3202N/A if not rules:
3202N/A return
3202N/A process_input = '\n'.join(rules) + '\n'
2605N/A cmd = ['/usr/sbin/ipf', '-r', '-f', '-']
2949N/A if version == 6:
2949N/A cmd.insert(1, '-6')
3202N/A self.execute_with_pfexec(cmd, process_input=process_input)
2605N/A
2605N/A
2900N/Aclass IPnatCommand(CommandBase):
2605N/A '''Wrapper around Solaris ipnat(1m) command'''
2605N/A
2605N/A def add_rules(self, rules):
3202N/A if not rules:
3202N/A return
3202N/A process_input = '\n'.join(rules) + '\n'
2605N/A cmd = ['/usr/sbin/ipnat', '-f', '-']
3202N/A self.execute_with_pfexec(cmd, process_input=process_input)
2605N/A
2605N/A def remove_rules(self, rules):
3202N/A if not rules:
3202N/A return
3202N/A process_input = '\n'.join(rules) + '\n'
2605N/A cmd = ['/usr/sbin/ipnat', '-r', '-f', '-']
3202N/A self.execute_with_pfexec(cmd, process_input=process_input)