Coverage for drivers/trim_util.py : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) Citrix Systems Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software Foundation, Inc.,
14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15#
16# A plugin for enabling trim on LVM based SRs to free up storage space
17# in Storage arrays.
19import xml
20import os
21import time
22import util
23import lock
24import lvhdutil
25import vhdutil
26import lvutil
28TRIM_LV_TAG = "_trim_lv"
29TRIM_CAP = "SR_TRIM"
30LOCK_RETRY_ATTEMPTS = 3
31LOCK_RETRY_INTERVAL = 1
32ERROR_CODE_KEY = "errcode"
33ERROR_MSG_KEY = "errmsg"
35TRIM_LAST_TRIGGERED_KEY = "trim_last_triggered"
36MASTER_LVM_CONF = '/etc/lvm/master'
39def _vg_by_sr_uuid(sr_uuid):
40 return lvhdutil.VG_PREFIX + sr_uuid
43def _lvpath_by_vg_lv_name(vg_name, lv_name):
44 return os.path.join(lvhdutil.VG_LOCATION, vg_name, lv_name)
47def to_xml(d):
49 dom = xml.dom.minidom.Document()
50 trim_response = dom.createElement("trim_response")
51 dom.appendChild(trim_response)
53 for key, value in sorted(d.items()):
54 key_value_element = dom.createElement("key_value_pair")
55 trim_response.appendChild(key_value_element)
57 key_element = dom.createElement("key")
58 key_text_node = dom.createTextNode(key)
59 key_element.appendChild(key_text_node)
60 key_value_element.appendChild(key_element)
62 value_element = dom.createElement("value")
63 value_text_mode = dom.createTextNode(value)
64 value_element.appendChild(value_text_mode)
65 key_value_element.appendChild(value_element)
67 return dom.toxml()
70def _handle_discard_error(vg_name, sr_uuid, error):
71 if "BLKDISCARD ioctl failed: Operation not supported" not in error.reason:
72 err_msg = {
73 ERROR_CODE_KEY: 'TrimException',
74 ERROR_MSG_KEY: error.reason
75 }
76 return to_xml(err_msg)
77 else:
78 return str(True)
81# Note: This function is expected to be called from a context where
82# the SR is locked by the thread calling the function; therefore removing
83# any risk of a race condition updating the LAST_TRIGGERED value.
84def _log_last_triggered(session, sr_uuid):
85 try:
86 sr_ref = session.xenapi.SR.get_by_uuid(sr_uuid)
87 other_config = session.xenapi.SR.get_other_config(sr_ref)
88 if TRIM_LAST_TRIGGERED_KEY in other_config:
89 session.xenapi.SR.remove_from_other_config(sr_ref, TRIM_LAST_TRIGGERED_KEY)
90 session.xenapi.SR.add_to_other_config(sr_ref, TRIM_LAST_TRIGGERED_KEY, str(time.time()))
91 except:
92 util.logException("Unable to set other-config:%s" % TRIM_LAST_TRIGGERED_KEY)
95def do_trim(session, args):
96 """Attempt to trim the given LVHDSR"""
97 util.SMlog("do_trim: %s" % args)
98 sr_uuid = args["sr_uuid"]
99 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
101 if TRIM_CAP not in util.sr_get_capability(sr_uuid):
102 util.SMlog("Trim command ignored on unsupported SR %s" % sr_uuid)
103 err_msg = {ERROR_CODE_KEY: 'UnsupportedSRForTrim',
104 ERROR_MSG_KEY: 'Trim on [%s] not supported' % sr_uuid}
105 return to_xml(err_msg)
107 # Lock SR, get vg empty space details
108 sr_lock = lock.Lock(vhdutil.LOCK_TYPE_SR, sr_uuid)
109 got_lock = False
110 for i in range(LOCK_RETRY_ATTEMPTS):
111 got_lock = sr_lock.acquireNoblock()
112 if got_lock:
113 break
114 time.sleep(LOCK_RETRY_INTERVAL)
116 if got_lock:
117 try:
118 vg_name = _vg_by_sr_uuid(sr_uuid)
119 lv_name = sr_uuid + TRIM_LV_TAG
120 lv_path = _lvpath_by_vg_lv_name(vg_name, lv_name)
122 # Clean trim LV in case the previous trim attemp failed
123 if lvutil.exists(lv_path):
124 lvutil.remove(lv_path)
126 #Check if VG limits are enough for creating LV.
127 stats = lvutil._getVGstats(vg_name)
128 if (stats['freespace'] < lvutil.LVM_SIZE_INCREMENT):
129 util.SMlog("No space to claim on a full SR %s" % sr_uuid)
130 err_msg = {ERROR_CODE_KEY: 'Trim failed on full SR',
131 ERROR_MSG_KEY: 'No space to claim on a full SR'}
132 result = to_xml(err_msg)
133 else:
134 # Perform a lvcreate, blkdiscard and lvremove to
135 # trigger trim on the array
136 lvutil.create(lv_name, 0, vg_name, size_in_percentage="100%F")
137 cmd = ["/usr/sbin/blkdiscard", "-v", lv_path]
138 stdout = util.pread2(cmd)
139 util.SMlog("Stdout is %s" % stdout)
140 util.SMlog("Trim on SR: %s complete. " % sr_uuid)
141 result = str(True)
142 except util.CommandException as e:
143 result = _handle_discard_error(vg_name, sr_uuid, e)
144 except:
145 err_msg = {
146 ERROR_CODE_KEY: 'UnknownTrimException',
147 ERROR_MSG_KEY: 'Unknown Exception: trim failed on SR [%s]'
148 % sr_uuid
149 }
150 result = to_xml(err_msg)
151 finally:
152 if lvutil.exists(lv_path):
153 lvutil.remove(lv_path)
155 _log_last_triggered(session, sr_uuid)
157 sr_lock.release()
158 return result
159 else:
160 util.SMlog("Could not complete Trim on %s, Lock unavailable !" \
161 % sr_uuid)
162 err_msg = {ERROR_CODE_KEY: 'SRUnavailable',
163 ERROR_MSG_KEY: 'Unable to get SR lock [%s]' % sr_uuid}
164 return to_xml(err_msg)