manila/manila/share/drivers/netapp/utils.py

286 lines
9.8 KiB
Python

# Copyright (c) 2015 Bob Callaway.
# Copyright (c) 2015 Tom Barron.
# 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.
"""
Utilities for NetApp drivers.
"""
import copy
import platform
import socket
from oslo.concurrency import processutils as putils
from oslo.utils import timeutils
from manila.i18n import _LI, _LW
from manila.openstack.common import log as logging
from manila.share.drivers.netapp import api as na_api
from manila import version
LOG = logging.getLogger(__name__)
def provide_ems(requester, server, netapp_backend, app_version,
server_type="cluster"):
"""Provide ems with volume stats for the requester.
"""
# TODO(tbarron): rework provide_ems to not store timestamp in the caller.
# This requires upcoming Manila NetApp refactoring work.
def _create_ems(netapp_backend, app_version, server_type):
"""Create ems api request."""
ems_log = na_api.NaElement('ems-autosupport-log')
host = socket.getfqdn() or 'Manila_node'
if server_type == "cluster":
dest = "cluster node"
else:
dest = "7 mode controller"
ems_log.add_new_child('computer-name', host)
ems_log.add_new_child('event-id', '0')
ems_log.add_new_child('event-source',
'Manila driver %s' % netapp_backend)
ems_log.add_new_child('app-version', app_version)
ems_log.add_new_child('category', 'provisioning')
ems_log.add_new_child('event-description',
'OpenStack Manila connected to %s' % dest)
ems_log.add_new_child('log-level', '6')
ems_log.add_new_child('auto-support', 'false')
return ems_log
def _create_vs_get():
"""Create vs_get api request."""
vs_get = na_api.NaElement('vserver-get-iter')
vs_get.add_new_child('max-records', '1')
query = na_api.NaElement('query')
query.add_node_with_children('vserver-info',
**{'vserver-type': 'node'})
vs_get.add_child_elem(query)
desired = na_api.NaElement('desired-attributes')
desired.add_node_with_children(
'vserver-info', **{'vserver-name': '', 'vserver-type': ''})
vs_get.add_child_elem(desired)
return vs_get
def _get_cluster_node(na_server):
"""Get the cluster node for ems."""
na_server.set_vserver(None)
vs_get = _create_vs_get()
res = na_server.invoke_successfully(vs_get)
if (res.get_child_content('num-records') and
int(res.get_child_content('num-records')) > 0):
attr_list = res.get_child_by_name('attributes-list')
vs_info = attr_list.get_child_by_name('vserver-info')
vs_name = vs_info.get_child_content('vserver-name')
return vs_name
return None
do_ems = True
if hasattr(requester, 'last_ems'):
sec_limit = 3559
if not (timeutils.is_older_than(requester.last_ems, sec_limit)):
do_ems = False
if do_ems:
na_server = copy.copy(server)
na_server.set_timeout(25)
ems = _create_ems(netapp_backend, app_version, server_type)
try:
if server_type == "cluster":
api_version = na_server.get_api_version()
if api_version:
major, minor = api_version
else:
raise na_api.NaApiError(code='Not found',
message='No api version found')
if major == 1 and minor > 15:
node = getattr(requester, 'vserver', None)
else:
node = _get_cluster_node(na_server)
if node is None:
raise na_api.NaApiError(code='Not found',
message='No vserver found')
na_server.set_vserver(node)
else:
na_server.set_vfiler(None)
na_server.invoke_successfully(ems, True)
LOG.debug("ems executed successfully.")
except na_api.NaApiError as e:
LOG.warn(_LW("Failed to invoke ems. Message : %s") % e)
finally:
requester.last_ems = timeutils.utcnow()
class OpenStackInfo(object):
"""OS/distribution, release, and version.
NetApp uses these fields as content for EMS log entry.
"""
PACKAGE_NAME = 'python-manila'
def __init__(self):
self._version = 'unknown version'
self._release = 'unknown release'
self._vendor = 'unknown vendor'
self._platform = 'unknown platform'
@property
def version(self):
return self._version
@version.setter
def version(self, value):
self._version = value
@property
def release(self):
return self._release
@release.setter
def release(self, value):
self._release = value
@property
def vendor(self):
return self._vendor
@vendor.setter
def vendor(self, value):
self._vendor = value
@property
def platform(self):
return self._platform
@platform.setter
def platform(self, value):
self._platform = value
# Because of the variety of platforms and OpenStack distributions that we
# may run against, it is by no means a sure thing that these update methods
# will work. We collect what information we can and deliberately ignore
# exceptions of any kind in order to avoid fillling the manila share log
# with noise.
def _update_version_from_version_string(self):
try:
self.version = version.version_info.version_string()
except Exception:
pass
def _update_release_from_release_string(self):
try:
self.release = version.version_info.release_string()
except Exception:
pass
def _update_platform(self):
try:
self.platform = platform.platform()
except Exception:
pass
@staticmethod
def _get_version_info_version():
return version.version_info.version
@staticmethod
def _get_version_info_release():
return version.version_info.release
def _update_info_from_version_info(self):
try:
ver = self._get_version_info_version()
if ver:
self.version = ver
except Exception:
pass
try:
rel = self._get_version_info_release()
if rel:
self.release = rel
except Exception:
pass
# RDO, RHEL-OSP, Mirantis on Redhat, SUSE
def _update_info_from_rpm(self):
LOG.debug('Trying rpm command.')
try:
out, err = putils.execute("rpm", "-q", "--queryformat",
"'%{version}\t%{release}\t%{vendor}'",
self.PACKAGE_NAME)
if not out:
LOG.info(_LI('No rpm info found for %(pkg)s package.') % {
'pkg': self.PACKAGE_NAME})
return False
parts = out.split()
self.version = parts[0]
self.release = parts[1]
self.vendor = ' '.join(parts[2::])
return True
except Exception as e:
LOG.info(_LI('Could not run rpm command: %(msg)s.') % {
'msg': e})
return False
# ubuntu, mirantis on ubuntu
def _update_info_from_dpkg(self):
LOG.debug('Trying dpkg-query command.')
try:
_vendor = None
out, err = putils.execute("dpkg-query", "-W", "-f='${Version}'",
self.PACKAGE_NAME)
if not out:
LOG.info(_LI(
'No dpkg-query info found for %(pkg)s package.') % {
'pkg': self.PACKAGE_NAME})
return False
# debian format: [epoch:]upstream_version[-debian_revision]
deb_version = out
# in case epoch or revision is missing, copy entire string
_release = deb_version
if ':' in deb_version:
deb_epoch, upstream_version = deb_version.split(':')
_release = upstream_version
if '-' in deb_version:
deb_revision = deb_version.split('-')[1]
_vendor = deb_revision
self.release = _release
if _vendor:
self.vendor = _vendor
return True
except Exception as e:
LOG.info(_LI('Could not run dpkg-query command: %(msg)s.') % {
'msg': e})
return False
def _update_openstack_info(self):
self._update_version_from_version_string()
self._update_release_from_release_string()
self._update_platform()
# some distributions override with more meaningful information
self._update_info_from_version_info()
# see if we have still more targeted info from rpm or apt
found_package = self._update_info_from_rpm()
if not found_package:
self._update_info_from_dpkg()
def info(self):
self._update_openstack_info()
return '%(version)s|%(release)s|%(vendor)s|%(platform)s' % {
'version': self.version, 'release': self.release,
'vendor': self.vendor, 'platform': self.platform}