Files
cinder/cinder/volume/throttling.py
Ivan Kolodyazhny ac2f6391e2 Remove exception.Error class
CinderException can be used as a low-level exception so we don't need to
introduce one more wrapper for Exception.

TSM backup driver modified without testing. Removed code was broken
because it tried to use not existing Exception class attributes.

Change-Id: Ie67d919f8f59572268f1f07946fb2d2f14a851f1
2020-07-21 18:21:21 +03:00

130 lines
4.1 KiB
Python

# Copyright (c) 2015 Hitachi Data Systems, Inc.
# 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.
"""Volume copy throttling helpers."""
import contextlib
from oslo_concurrency import processutils
from oslo_log import log as logging
from cinder import exception
import cinder.privsep.cgroup
from cinder import utils
LOG = logging.getLogger(__name__)
class Throttle(object):
"""Base class for throttling disk I/O bandwidth"""
DEFAULT = None
@staticmethod
def set_default(throttle):
Throttle.DEFAULT = throttle
@staticmethod
def get_default():
return Throttle.DEFAULT or Throttle()
def __init__(self, prefix=None):
self.prefix = prefix or []
@contextlib.contextmanager
def subcommand(self, srcpath, dstpath):
"""Sub-command that reads from srcpath and writes to dstpath.
Throttle disk I/O bandwidth used by a sub-command, such as 'dd',
that reads from srcpath and writes to dstpath. The sub-command
must be executed with the generated prefix command.
"""
yield {'prefix': self.prefix}
class BlkioCgroup(Throttle):
"""Throttle disk I/O bandwidth using blkio cgroups."""
def __init__(self, bps_limit, cgroup_name):
self.bps_limit = bps_limit
self.cgroup = cgroup_name
self.srcdevs = {}
self.dstdevs = {}
try:
cinder.privsep.cgroup.cgroup_create(self.cgroup)
except processutils.ProcessExecutionError:
LOG.error('Failed to create blkio cgroup \'%(name)s\'.',
{'name': cgroup_name})
raise
def _get_device_number(self, path):
try:
return utils.get_blkdev_major_minor(path)
except exception.CinderException as e:
LOG.error('Failed to get device number for throttling: '
'%(error)s', {'error': e})
def _limit_bps(self, rw, dev, bps):
try:
cinder.privsep.cgroup.cgroup_limit(self.cgroup, rw, dev, bps)
except processutils.ProcessExecutionError:
LOG.warning('Failed to setup blkio cgroup to throttle the '
'device \'%(device)s\'.', {'device': dev})
def _set_limits(self, rw, devs):
total = sum(devs.values())
for dev in sorted(devs):
self._limit_bps(rw, dev, self.bps_limit * devs[dev] / total)
@utils.synchronized('BlkioCgroup')
def _inc_device(self, srcdev, dstdev):
if srcdev:
self.srcdevs[srcdev] = self.srcdevs.get(srcdev, 0) + 1
self._set_limits('read', self.srcdevs)
if dstdev:
self.dstdevs[dstdev] = self.dstdevs.get(dstdev, 0) + 1
self._set_limits('write', self.dstdevs)
@utils.synchronized('BlkioCgroup')
def _dec_device(self, srcdev, dstdev):
if srcdev:
self.srcdevs[srcdev] -= 1
if self.srcdevs[srcdev] == 0:
del self.srcdevs[srcdev]
self._set_limits('read', self.srcdevs)
if dstdev:
self.dstdevs[dstdev] -= 1
if self.dstdevs[dstdev] == 0:
del self.dstdevs[dstdev]
self._set_limits('write', self.dstdevs)
@contextlib.contextmanager
def subcommand(self, srcpath, dstpath):
srcdev = self._get_device_number(srcpath)
dstdev = self._get_device_number(dstpath)
if srcdev is None and dstdev is None:
yield {'prefix': []}
return
self._inc_device(srcdev, dstdev)
try:
yield {'prefix': ['cgexec', '-g', 'blkio:%s' % self.cgroup]}
finally:
self._dec_device(srcdev, dstdev)