¡@

Home 

OpenStack Study: ssh.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2014 IBM Corp.

# 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.

#

import re

from cinder import exception

from cinder.openstack.common import log as logging

from cinder.openstack.common import processutils

LOG = logging.getLogger(__name__)

**** CubicPower OpenStack Study ****

class StorwizeSSH(object):

"""SSH interface to IBM Storwize family and SVC storage systems."""

**** CubicPower OpenStack Study ****

    def __init__(self, run_ssh):

        self._ssh = run_ssh

**** CubicPower OpenStack Study ****

    def _run_ssh(self, ssh_cmd):

        try:

            return self._ssh(ssh_cmd)

        except processutils.ProcessExecutionError as e:

            msg = (_('CLI Exception output:\n command: %(cmd)s\n '

                     'stdout: %(out)s\n stderr: %(err)s') %

                   {'cmd': ssh_cmd,

                    'out': e.stdout,

                    'err': e.stderr})

            LOG.error(msg)

            raise exception.VolumeBackendAPIException(data=msg)

**** CubicPower OpenStack Study ****

    def run_ssh_info(self, ssh_cmd, delim='!', with_header=False):

        """Run an SSH command and return parsed output."""

        raw = self._run_ssh(ssh_cmd)

        return CLIResponse(raw, ssh_cmd=ssh_cmd, delim=delim,

                           with_header=with_header)

**** CubicPower OpenStack Study ****

    def run_ssh_assert_no_output(self, ssh_cmd):

        """Run an SSH command and assert no output returned."""

        out, err = self._run_ssh(ssh_cmd)

        if len(out.strip()) != 0:

            msg = (_('Expected no output from CLI command %(cmd)s, '

                     'got %(out)s') % {'cmd': ' '.join(ssh_cmd), 'out': out})

            LOG.error(msg)

            raise exception.VolumeBackendAPIException(data=msg)

**** CubicPower OpenStack Study ****

    def run_ssh_check_created(self, ssh_cmd):

        """Run an SSH command and return the ID of the created object."""

        out, err = self._run_ssh(ssh_cmd)

        try:

            match_obj = re.search(r'\[([0-9]+)\],? successfully created', out)

            return match_obj.group(1)

        except (AttributeError, IndexError):

            msg = (_('Failed to parse CLI output:\n command: %(cmd)s\n '

                     'stdout: %(out)s\n stderr: %(err)s') %

                   {'cmd': ssh_cmd,

                    'out': out,

                    'err': err})

            LOG.error(msg)

            raise exception.VolumeBackendAPIException(data=msg)

**** CubicPower OpenStack Study ****

    def lsnode(self, node_id=None):

        with_header = True

        ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!']

        if node_id:

            with_header = False

            ssh_cmd.append(node_id)

        return self.run_ssh_info(ssh_cmd, with_header=with_header)

**** CubicPower OpenStack Study ****

    def lslicense(self):

        ssh_cmd = ['svcinfo', 'lslicense', '-delim', '!']

        return self.run_ssh_info(ssh_cmd)[0]

**** CubicPower OpenStack Study ****

    def lssystem(self):

        ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!']

        return self.run_ssh_info(ssh_cmd)[0]

**** CubicPower OpenStack Study ****

    def lsmdiskgrp(self, pool):

        ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]

        return self.run_ssh_info(ssh_cmd)[0]

**** CubicPower OpenStack Study ****

    def lsiogrp(self):

        ssh_cmd = ['svcinfo', 'lsiogrp', '-delim', '!']

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def lsportip(self):

        ssh_cmd = ['svcinfo', 'lsportip', '-delim', '!']

        return self.run_ssh_info(ssh_cmd, with_header=True)

    @staticmethod

**** CubicPower OpenStack Study ****

    def _create_port_arg(port_type, port_name):

        if port_type == 'initiator':

            port = ['-iscsiname']

        else:

            port = ['-hbawwpn']

        port.append(port_name)

        return port

**** CubicPower OpenStack Study ****

    def mkhost(self, host_name, port_type, port_name):

        port = self._create_port_arg(port_type, port_name)

        ssh_cmd = ['svctask', 'mkhost', '-force'] + port

        ssh_cmd += ['-name', '"%s"' % host_name]

        return self.run_ssh_check_created(ssh_cmd)

**** CubicPower OpenStack Study ****

    def addhostport(self, host, port_type, port_name):

        port = self._create_port_arg(port_type, port_name)

        ssh_cmd = ['svctask', 'addhostport', '-force'] + port + ['"%s"' % host]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lshost(self, host=None):

        with_header = True

        ssh_cmd = ['svcinfo', 'lshost', '-delim', '!']

        if host:

            with_header = False

            ssh_cmd.append('"%s"' % host)

        return self.run_ssh_info(ssh_cmd, with_header=with_header)

**** CubicPower OpenStack Study ****

    def add_chap_secret(self, secret, host):

        ssh_cmd = ['svctask', 'chhost', '-chapsecret', secret, '"%s"' % host]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lsiscsiauth(self):

        ssh_cmd = ['svcinfo', 'lsiscsiauth', '-delim', '!']

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def lsfabric(self, wwpn=None, host=None):

        ssh_cmd = ['svcinfo', 'lsfabric', '-delim', '!']

        if wwpn:

            ssh_cmd.extend(['-wwpn', wwpn])

        elif host:

            ssh_cmd.extend(['-host', '"%s"' % host])

        else:

            msg = (_('Must pass wwpn or host to lsfabric.'))

            LOG.error(msg)

            raise exception.VolumeDriverException(message=msg)

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def mkvdiskhostmap(self, host, vdisk, lun, multihostmap):

        """Map vdisk to host.

        If vdisk already mapped and multihostmap is True, use the force flag.

        """

        ssh_cmd = ['svctask', 'mkvdiskhostmap', '-host', '"%s"' % host,

                   '-scsi', lun, vdisk]

        out, err = self._ssh(ssh_cmd, check_exit_code=False)

        if 'successfully created' in out:

            return

        if not err:

            msg = (_('Did not find success message nor error for %(fun)s: '

                     '%(out)s') % {'out': out, 'fun': ssh_cmd})

            raise exception.VolumeBackendAPIException(data=msg)

        if err.startswith('CMMVC6071E'):

            if not multihostmap:

                LOG.error(_('storwize_svc_multihostmap_enabled is set '

                            'to False, not allowing multi host mapping.'))

                msg = 'CMMVC6071E The VDisk-to-host mapping '\

                      'was not created because the VDisk is '\

                      'already mapped to a host.\n"'

                raise exception.VolumeDriverException(message=msg)

        ssh_cmd.insert(ssh_cmd.index('mkvdiskhostmap') + 1, '-force')

        return self.run_ssh_check_created(ssh_cmd)

**** CubicPower OpenStack Study ****

    def rmvdiskhostmap(self, host, vdisk):

        ssh_cmd = ['svctask', 'rmvdiskhostmap', '-host', '"%s"' % host, vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lsvdiskhostmap(self, vdisk):

        ssh_cmd = ['svcinfo', 'lsvdiskhostmap', '-delim', '!', vdisk]

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def lshostvdiskmap(self, host):

        ssh_cmd = ['svcinfo', 'lshostvdiskmap', '-delim', '!', '"%s"' % host]

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def rmhost(self, host):

        ssh_cmd = ['svctask', 'rmhost', '"%s"' % host]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def mkvdisk(self, name, size, units, pool, opts, params):

        ssh_cmd = ['svctask', 'mkvdisk', '-name', name, '-mdiskgrp', pool,

                   '-iogrp', str(opts['iogrp']), '-size', size, '-unit',

                   units] + params

        return self.run_ssh_check_created(ssh_cmd)

**** CubicPower OpenStack Study ****

    def rmvdisk(self, vdisk, force=True):

        ssh_cmd = ['svctask', 'rmvdisk']

        if force:

            ssh_cmd += ['-force']

        ssh_cmd += [vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lsvdisk(self, vdisk):

        """Return vdisk attributes or None if it doesn't exist."""

        ssh_cmd = ['svcinfo', 'lsvdisk', '-bytes', '-delim', '!', vdisk]

        out, err = self._ssh(ssh_cmd, check_exit_code=False)

        if not len(err):

            return CLIResponse((out, err), ssh_cmd=ssh_cmd, delim='!',

                               with_header=False)[0]

        if err.startswith('CMMVC5754E'):

            return None

        msg = (_('CLI Exception output:\n command: %(cmd)s\n '

                 'stdout: %(out)s\n stderr: %(err)s') %

               {'cmd': ssh_cmd,

                'out': out,

                'err': err})

        LOG.error(msg)

        raise exception.VolumeBackendAPIException(data=msg)

**** CubicPower OpenStack Study ****

    def lsvdisks_from_filter(self, filter_name, value):

        """Performs an lsvdisk command, filtering the results as specified.

        Returns an iterable for all matching vdisks.

        """

        ssh_cmd = ['svcinfo', 'lsvdisk', '-bytes', '-delim', '!',

                   '-filtervalue', '%s=%s' % (filter_name, value)]

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def chvdisk(self, vdisk, params):

        ssh_cmd = ['svctask', 'chvdisk'] + params + [vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def movevdisk(self, vdisk, iogrp):

        ssh_cmd = ['svctask', 'movevdisk', '-iogrp', iogrp, vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def expandvdisksize(self, vdisk, amount):

        ssh_cmd = (['svctask', 'expandvdisksize', '-size', str(amount),

                    '-unit', 'gb', vdisk])

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def mkfcmap(self, source, target, full_copy):

        ssh_cmd = ['svctask', 'mkfcmap', '-source', source, '-target',

                   target, '-autodelete']

        if not full_copy:

            ssh_cmd.extend(['-copyrate', '0'])

        out, err = self._ssh(ssh_cmd, check_exit_code=False)

        if 'successfully created' not in out:

            msg = (_('CLI Exception output:\n command: %(cmd)s\n '

                     'stdout: %(out)s\n stderr: %(err)s') %

                   {'cmd': ssh_cmd,

                    'out': out,

                    'err': err})

            LOG.error(msg)

            raise exception.VolumeBackendAPIException(data=msg)

        try:

            match_obj = re.search(r'FlashCopy Mapping, id \[([0-9]+)\], '

                                  'successfully created', out)

            fc_map_id = match_obj.group(1)

        except (AttributeError, IndexError):

            msg = (_('Failed to parse CLI output:\n command: %(cmd)s\n '

                     'stdout: %(out)s\n stderr: %(err)s') %

                   {'cmd': ssh_cmd,

                    'out': out,

                    'err': err})

            LOG.error(msg)

            raise exception.VolumeBackendAPIException(data=msg)

        return fc_map_id

**** CubicPower OpenStack Study ****

    def prestartfcmap(self, fc_map_id):

        ssh_cmd = ['svctask', 'prestartfcmap', fc_map_id]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def startfcmap(self, fc_map_id):

        ssh_cmd = ['svctask', 'startfcmap', fc_map_id]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def chfcmap(self, fc_map_id, copyrate='50', autodel='on'):

        ssh_cmd = ['svctask', 'chfcmap', '-copyrate', copyrate,

                   '-autodelete', autodel, fc_map_id]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def stopfcmap(self, fc_map_id):

        ssh_cmd = ['svctask', 'stopfcmap', fc_map_id]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def rmfcmap(self, fc_map_id):

        ssh_cmd = ['svctask', 'rmfcmap', '-force', fc_map_id]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lsvdiskfcmappings(self, vdisk):

        ssh_cmd = ['svcinfo', 'lsvdiskfcmappings', '-delim', '!', vdisk]

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def lsfcmap(self, fc_map_id):

        ssh_cmd = ['svcinfo', 'lsfcmap', '-filtervalue',

                   'id=%s' % fc_map_id, '-delim', '!']

        return self.run_ssh_info(ssh_cmd, with_header=True)

**** CubicPower OpenStack Study ****

    def addvdiskcopy(self, vdisk, dest_pool, params):

        ssh_cmd = (['svctask', 'addvdiskcopy'] + params + ['-mdiskgrp',

                   dest_pool, vdisk])

        return self.run_ssh_check_created(ssh_cmd)

**** CubicPower OpenStack Study ****

    def lsvdiskcopy(self, vdisk, copy_id=None):

        ssh_cmd = ['svcinfo', 'lsvdiskcopy', '-delim', '!']

        with_header = True

        if copy_id:

            ssh_cmd += ['-copy', copy_id]

            with_header = False

        ssh_cmd += [vdisk]

        return self.run_ssh_info(ssh_cmd, with_header=with_header)

**** CubicPower OpenStack Study ****

    def rmvdiskcopy(self, vdisk, copy_id):

        ssh_cmd = ['svctask', 'rmvdiskcopy', '-copy', copy_id, vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def addvdiskaccess(self, vdisk, iogrp):

        ssh_cmd = ['svctask', 'addvdiskaccess', '-iogrp', iogrp, vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

    def rmvdiskaccess(self, vdisk, iogrp):

        ssh_cmd = ['svctask', 'rmvdiskaccess', '-iogrp', iogrp, vdisk]

        self.run_ssh_assert_no_output(ssh_cmd)

**** CubicPower OpenStack Study ****

class CLIResponse(object):

'''Parse SVC CLI output and generate iterable.'''

**** CubicPower OpenStack Study ****

    def __init__(self, raw, ssh_cmd=None, delim='!', with_header=True):

        super(CLIResponse, self).__init__()

        if ssh_cmd:

            self.ssh_cmd = ' '.join(ssh_cmd)

        else:

            self.ssh_cmd = 'None'

        self.raw = raw

        self.delim = delim

        self.with_header = with_header

        self.result = self._parse()

**** CubicPower OpenStack Study ****

    def select(self, *keys):

        for a in self.result:

            vs = []

            for k in keys:

                v = a.get(k, None)

                if isinstance(v, basestring) or v is None:

                    v = [v]

                if isinstance(v, list):

                    vs.append(v)

            for item in zip(*vs):

                if len(item) == 1:

                    yield item[0]

                else:

                    yield item

**** CubicPower OpenStack Study ****

    def __getitem__(self, key):

        try:

            return self.result[key]

        except KeyError:

            msg = (_('Did not find expected key %(key)s in %(fun)s: %(raw)s') %

                   {'key': key, 'fun': self.ssh_cmd, 'raw': self.raw})

            raise exception.VolumeBackendAPIException(data=msg)

**** CubicPower OpenStack Study ****

    def __iter__(self):

        for a in self.result:

            yield a

**** CubicPower OpenStack Study ****

    def __len__(self):

        return len(self.result)

**** CubicPower OpenStack Study ****

    def _parse(self):

        def get_reader(content, delim):

            for line in content.lstrip().splitlines():

                line = line.strip()

                if line:

                    yield line.split(delim)

                else:

                    yield []

        if isinstance(self.raw, basestring):

            stdout, stderr = self.raw, ''

        else:

            stdout, stderr = self.raw

        reader = get_reader(stdout, self.delim)

        result = []

        if self.with_header:

            hds = tuple()

            for row in reader:

                hds = row

                break

            for row in reader:

                cur = dict()

                if len(hds) != len(row):

                    msg = (_('Unexpected CLI response: header/row mismatch. '

                             'header: %(header)s, row: %(row)s')

                           % {'header': hds,

                              'row': row})

                    raise exception.VolumeBackendAPIException(data=msg)

                for k, v in zip(hds, row):

                    CLIResponse.append_dict(cur, k, v)

                result.append(cur)

        else:

            cur = dict()

            for row in reader:

                if row:

                    CLIResponse.append_dict(cur, row[0], ' '.join(row[1:]))

                elif cur:  # start new section

                    result.append(cur)

                    cur = dict()

            if cur:

                result.append(cur)

        return result

    @staticmethod

**** CubicPower OpenStack Study ****

        def get_reader(content, delim):

            for line in content.lstrip().splitlines():

                line = line.strip()

                if line:

                    yield line.split(delim)

                else:

                    yield []

        if isinstance(self.raw, basestring):

            stdout, stderr = self.raw, ''

        else:

            stdout, stderr = self.raw

        reader = get_reader(stdout, self.delim)

        result = []

        if self.with_header:

            hds = tuple()

            for row in reader:

                hds = row

                break

            for row in reader:

                cur = dict()

                if len(hds) != len(row):

                    msg = (_('Unexpected CLI response: header/row mismatch. '

                             'header: %(header)s, row: %(row)s')

                           % {'header': hds,

                              'row': row})

                    raise exception.VolumeBackendAPIException(data=msg)

                for k, v in zip(hds, row):

                    CLIResponse.append_dict(cur, k, v)

                result.append(cur)

        else:

            cur = dict()

            for row in reader:

                if row:

                    CLIResponse.append_dict(cur, row[0], ' '.join(row[1:]))

                elif cur:  # start new section

                    result.append(cur)

                    cur = dict()

            if cur:

                result.append(cur)

        return result

    @staticmethod

**** CubicPower OpenStack Study ****

    def append_dict(dict_, key, value):

        key, value = key.strip(), value.strip()

        obj = dict_.get(key, None)

        if obj is None:

            dict_[key] = value

        elif isinstance(obj, list):

            obj.append(value)

            dict_[key] = obj

        else:

            dict_[key] = [obj, value]

        return dict_