root / svc_extstorage / storwize_svc.py @ 2eba2338
History | View | Annotate | Download (31.7 kB)
1 | 2eba2338 | Stratos Psomadakis | # vim: tabstop=4 shiftwidth=4 softtabstop=4
|
---|---|---|---|
2 | 2eba2338 | Stratos Psomadakis | |
3 | 2eba2338 | Stratos Psomadakis | # Copyright 2013 GRNET S.A.
|
4 | 2eba2338 | Stratos Psomadakis | # Copyright 2013 IBM Corp.
|
5 | 2eba2338 | Stratos Psomadakis | # Copyright 2012 OpenStack LLC.
|
6 | 2eba2338 | Stratos Psomadakis | # All Rights Reserved.
|
7 | 2eba2338 | Stratos Psomadakis | #
|
8 | 2eba2338 | Stratos Psomadakis | # Licensed under the Apache License, Version 2.0 (the "License"); you may
|
9 | 2eba2338 | Stratos Psomadakis | # not use this file except in compliance with the License. You may obtain
|
10 | 2eba2338 | Stratos Psomadakis | # a copy of the License at
|
11 | 2eba2338 | Stratos Psomadakis | #
|
12 | 2eba2338 | Stratos Psomadakis | # http://www.apache.org/licenses/LICENSE-2.0
|
13 | 2eba2338 | Stratos Psomadakis | #
|
14 | 2eba2338 | Stratos Psomadakis | # Unless required by applicable law or agreed to in writing, software
|
15 | 2eba2338 | Stratos Psomadakis | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
16 | 2eba2338 | Stratos Psomadakis | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
17 | 2eba2338 | Stratos Psomadakis | # License for the specific language governing permissions and limitations
|
18 | 2eba2338 | Stratos Psomadakis | # under the License.
|
19 | 2eba2338 | Stratos Psomadakis | #
|
20 | 2eba2338 | Stratos Psomadakis | # Authors:
|
21 | 2eba2338 | Stratos Psomadakis | # Stratos Psomadakis <psomas@grnet.gr>
|
22 | 2eba2338 | Stratos Psomadakis | # Ronen Kat <ronenkat@il.ibm.com>
|
23 | 2eba2338 | Stratos Psomadakis | # Avishay Traeger <avishay@il.ibm.com>
|
24 | 2eba2338 | Stratos Psomadakis | |
25 | 2eba2338 | Stratos Psomadakis | """ Ganeti extstorage provider for IBM Storwize family and SVC storage systems
|
26 | 2eba2338 | Stratos Psomadakis | (ported from the Cinder Storwize / SVC volume driver).
|
27 | 2eba2338 | Stratos Psomadakis |
|
28 | 2eba2338 | Stratos Psomadakis | The script takes its input from environment variables. Specifically the
|
29 | 2eba2338 | Stratos Psomadakis | following variables should be present:
|
30 | 2eba2338 | Stratos Psomadakis |
|
31 | 2eba2338 | Stratos Psomadakis | - VOL_NAME: The name of the new Image file
|
32 | 2eba2338 | Stratos Psomadakis | - EXTP_SVC_SAN: The provider's configuration file section / identifier for the
|
33 | 2eba2338 | Stratos Psomadakis | SVC SAN
|
34 | 2eba2338 | Stratos Psomadakis |
|
35 | 2eba2338 | Stratos Psomadakis | The following environmental variables are optional and should be present only
|
36 | 2eba2338 | Stratos Psomadakis | for specific commands:
|
37 | 2eba2338 | Stratos Psomadakis |
|
38 | 2eba2338 | Stratos Psomadakis | - VOL_SIZE: The size of the Image file
|
39 | 2eba2338 | Stratos Psomadakis |
|
40 | 2eba2338 | Stratos Psomadakis | Based on the EXTP_SVC_SAN variable, the script will also read the SVC
|
41 | 2eba2338 | Stratos Psomadakis | configration parameters / options from the corresponding section of the INI SVC
|
42 | 2eba2338 | Stratos Psomadakis | configuration file. Currently, the config file path is hardcoded in
|
43 | 2eba2338 | Stratos Psomadakis | SVC_CONFIG_PATH (/etc/ganeti/extstorage/svc.conf). Each SAN / SVC section of
|
44 | 2eba2338 | Stratos Psomadakis | the INI file, which should set the following options:
|
45 | 2eba2338 | Stratos Psomadakis | - host: The management hostname / IP for the SVC
|
46 | 2eba2338 | Stratos Psomadakis | - port: The SSH daemon port for the SVC
|
47 | 2eba2338 | Stratos Psomadakis | - user: The user to use to connect (SSH) to the SVC
|
48 | 2eba2338 | Stratos Psomadakis | - password: The SSH password for the user
|
49 | 2eba2338 | Stratos Psomadakis | - pool: The SVC volume pool to use to create vdisks / volumes
|
50 | 2eba2338 | Stratos Psomadakis | - host_id: The id of the host in the SVC hosts table
|
51 | 2eba2338 | Stratos Psomadakis |
|
52 | 2eba2338 | Stratos Psomadakis | Example SAN SVC INI config file:
|
53 | 2eba2338 | Stratos Psomadakis | [san1]
|
54 | 2eba2338 | Stratos Psomadakis | host = mysvchost1
|
55 | 2eba2338 | Stratos Psomadakis | port = mysvcport1
|
56 | 2eba2338 | Stratos Psomadakis | user = mysvcuser1
|
57 | 2eba2338 | Stratos Psomadakis | password = mysvcpass1
|
58 | 2eba2338 | Stratos Psomadakis | pool = mypool1
|
59 | 2eba2338 | Stratos Psomadakis | host_id = mysvchostid1
|
60 | 2eba2338 | Stratos Psomadakis | [san2]
|
61 | 2eba2338 | Stratos Psomadakis | host = mysvchost2
|
62 | 2eba2338 | Stratos Psomadakis | port = mysvcport2
|
63 | 2eba2338 | Stratos Psomadakis | user = mysvcuser2
|
64 | 2eba2338 | Stratos Psomadakis | password = mysvcpass2
|
65 | 2eba2338 | Stratos Psomadakis | pool = mypool2
|
66 | 2eba2338 | Stratos Psomadakis | host_id = mysvchostid2
|
67 | 2eba2338 | Stratos Psomadakis |
|
68 | 2eba2338 | Stratos Psomadakis | The code branches to the correct function, depending on the name (sys.argv[0])
|
69 | 2eba2338 | Stratos Psomadakis | of the executed script (attach, create, etc.).
|
70 | 2eba2338 | Stratos Psomadakis |
|
71 | 2eba2338 | Stratos Psomadakis | Returns O after successful completion, 1 on failure.
|
72 | 2eba2338 | Stratos Psomadakis |
|
73 | 2eba2338 | Stratos Psomadakis | Limitations:
|
74 | 2eba2338 | Stratos Psomadakis | 1. The provider expects CLI output in English, error messages may be in a
|
75 | 2eba2338 | Stratos Psomadakis | localized format.
|
76 | 2eba2338 | Stratos Psomadakis | 2. iSCSI support, public key authentication and detailed configuration for the
|
77 | 2eba2338 | Stratos Psomadakis | SVC pool, volumes etc. not implemented yet.
|
78 | 2eba2338 | Stratos Psomadakis | 3. The current version of the provider makes lots of assumptions about the
|
79 | 2eba2338 | Stratos Psomadakis | running environment. It needs to be run on a Debian-like distro with various
|
80 | 2eba2338 | Stratos Psomadakis | SCSI tools installed and multipath support as well. Multipath udev triggers
|
81 | 2eba2338 | Stratos Psomadakis | should be removed, to ensure reliable operation of the provider.
|
82 | 2eba2338 | Stratos Psomadakis |
|
83 | 2eba2338 | Stratos Psomadakis | """
|
84 | 2eba2338 | Stratos Psomadakis | |
85 | 2eba2338 | Stratos Psomadakis | import ConfigParser |
86 | 2eba2338 | Stratos Psomadakis | import glob |
87 | 2eba2338 | Stratos Psomadakis | import os |
88 | 2eba2338 | Stratos Psomadakis | import re |
89 | 2eba2338 | Stratos Psomadakis | import subprocess |
90 | 2eba2338 | Stratos Psomadakis | import sys |
91 | 2eba2338 | Stratos Psomadakis | import traceback |
92 | 2eba2338 | Stratos Psomadakis | |
93 | 2eba2338 | Stratos Psomadakis | import paramiko |
94 | 2eba2338 | Stratos Psomadakis | |
95 | 2eba2338 | Stratos Psomadakis | |
96 | 2eba2338 | Stratos Psomadakis | # Hardcoded path to the SVC (Ganeti) config file
|
97 | 2eba2338 | Stratos Psomadakis | _SVC_CONFIG_PATH = "/etc/ganeti/extstorage/svc.conf"
|
98 | 2eba2338 | Stratos Psomadakis | |
99 | 2eba2338 | Stratos Psomadakis | # Hardcoded pool name, used for assertions
|
100 | 2eba2338 | Stratos Psomadakis | _SVC_POOL_NAME = "VM_POOL_SYNNEFO"
|
101 | 2eba2338 | Stratos Psomadakis | |
102 | 2eba2338 | Stratos Psomadakis | # Hardcoded host id range for HDIKA Synnefo nodes, used for assertions
|
103 | 2eba2338 | Stratos Psomadakis | _SVC_VALID_RANGE = range(7, 12) + range(19, 23) |
104 | 2eba2338 | Stratos Psomadakis | |
105 | 2eba2338 | Stratos Psomadakis | # Hardcoded timeout for SSH (paramiko), in seconds
|
106 | 2eba2338 | Stratos Psomadakis | SVC_SSH_TIMEOUT = 30
|
107 | 2eba2338 | Stratos Psomadakis | |
108 | 2eba2338 | Stratos Psomadakis | # sysfs FC-related paths and tools, needed for adding (rescanning) and deleting
|
109 | 2eba2338 | Stratos Psomadakis | # FC disks / volumes.
|
110 | 2eba2338 | Stratos Psomadakis | _RESCAN_SCSI_CMD = 'rescan-scsi-bus'
|
111 | 2eba2338 | Stratos Psomadakis | _MULTIPATH_CMD = 'multipath'
|
112 | 2eba2338 | Stratos Psomadakis | _DMSETUP_CMD = 'dmsetup'
|
113 | 2eba2338 | Stratos Psomadakis | # Might be needing these below, in the future
|
114 | 2eba2338 | Stratos Psomadakis | #_SYSTOOL_CMD = 'systool'
|
115 | 2eba2338 | Stratos Psomadakis | #_SYSFS_FCHOSTS_PATH = '/sys/class/fc_host/'
|
116 | 2eba2338 | Stratos Psomadakis | #_SYSFS_FCSCAN_PATH = '/sys/class/scsi_host/%(fchost)s/scan'
|
117 | 2eba2338 | Stratos Psomadakis | #_SYSFS_FCSCAN_COMMAND = '- - -'
|
118 | 2eba2338 | Stratos Psomadakis | _SYSFS_BLOCK_PATH = '/sys/block/%(blodkdev)s/'
|
119 | 2eba2338 | Stratos Psomadakis | _SYSFS_FCDELETE_PATH = os.path.join(_SYSFS_BLOCK_PATH, '/device/delete')
|
120 | 2eba2338 | Stratos Psomadakis | _SYSFS_SLAVES_PATH = os.path.join(_SYSFS_BLOCK_PATH, '/slaves/')
|
121 | 2eba2338 | Stratos Psomadakis | _DEV_BYPATH = '/dev/disk/by-path/'
|
122 | 2eba2338 | Stratos Psomadakis | |
123 | 2eba2338 | Stratos Psomadakis | _SCSI_DEV_PREFIX = '3'
|
124 | 2eba2338 | Stratos Psomadakis | |
125 | 2eba2338 | Stratos Psomadakis | |
126 | 2eba2338 | Stratos Psomadakis | class SVCError(Exception): |
127 | 2eba2338 | Stratos Psomadakis | """ Base exception for SVC extstorage provider.
|
128 | 2eba2338 | Stratos Psomadakis |
|
129 | 2eba2338 | Stratos Psomadakis | """
|
130 | 2eba2338 | Stratos Psomadakis | pass
|
131 | 2eba2338 | Stratos Psomadakis | |
132 | 2eba2338 | Stratos Psomadakis | |
133 | 2eba2338 | Stratos Psomadakis | class SVCConnection(object): |
134 | 2eba2338 | Stratos Psomadakis | """ SSH connection (paramiko) abstraction class.
|
135 | 2eba2338 | Stratos Psomadakis |
|
136 | 2eba2338 | Stratos Psomadakis | """
|
137 | 2eba2338 | Stratos Psomadakis | def __init__(self, host, port, user, password): |
138 | 2eba2338 | Stratos Psomadakis | """ Initialize the SVCConnection object
|
139 | 2eba2338 | Stratos Psomadakis |
|
140 | 2eba2338 | Stratos Psomadakis | @type host: string
|
141 | 2eba2338 | Stratos Psomadakis | @param host: host to connect to
|
142 | 2eba2338 | Stratos Psomadakis | @type port: integer
|
143 | 2eba2338 | Stratos Psomadakis | @param port: SSH port to connect to
|
144 | 2eba2338 | Stratos Psomadakis | @type user: string
|
145 | 2eba2338 | Stratos Psomadakis | @param user: SSH user to use
|
146 | 2eba2338 | Stratos Psomadakis | @type password: string
|
147 | 2eba2338 | Stratos Psomadakis | @param password: SSH password to use
|
148 | 2eba2338 | Stratos Psomadakis |
|
149 | 2eba2338 | Stratos Psomadakis | """
|
150 | 2eba2338 | Stratos Psomadakis | super(SVCConnection, self).__init__() |
151 | 2eba2338 | Stratos Psomadakis | |
152 | 2eba2338 | Stratos Psomadakis | self._host = host
|
153 | 2eba2338 | Stratos Psomadakis | self._port = port
|
154 | 2eba2338 | Stratos Psomadakis | self._user = user
|
155 | 2eba2338 | Stratos Psomadakis | self._password = password
|
156 | 2eba2338 | Stratos Psomadakis | |
157 | 2eba2338 | Stratos Psomadakis | self._conn = None |
158 | 2eba2338 | Stratos Psomadakis | |
159 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
160 | 2eba2338 | Stratos Psomadakis | def _check_ssh_injection(cmd_list): |
161 | 2eba2338 | Stratos Psomadakis | """ Check for ssh injection patterns in the cmd list.
|
162 | 2eba2338 | Stratos Psomadakis |
|
163 | 2eba2338 | Stratos Psomadakis | @type cmd_list: list of strings
|
164 | 2eba2338 | Stratos Psomadakis | @param cmd_list: a list containing the command to be executed plus any
|
165 | 2eba2338 | Stratos Psomadakis | arguments to the command
|
166 | 2eba2338 | Stratos Psomadakis | """
|
167 | 2eba2338 | Stratos Psomadakis | ssh_injection_pattern = ['`', '$', '|', '||', ';', '&', '&&', '>', |
168 | 2eba2338 | Stratos Psomadakis | '>>', '<'] |
169 | 2eba2338 | Stratos Psomadakis | |
170 | 2eba2338 | Stratos Psomadakis | # Check whether injection attacks exist
|
171 | 2eba2338 | Stratos Psomadakis | for arg in cmd_list: |
172 | 2eba2338 | Stratos Psomadakis | arg = arg.strip() |
173 | 2eba2338 | Stratos Psomadakis | # First, check no space in the middle of arg
|
174 | 2eba2338 | Stratos Psomadakis | arg_len = len(arg.split())
|
175 | 2eba2338 | Stratos Psomadakis | if arg_len > 1: |
176 | 2eba2338 | Stratos Psomadakis | raise SVCError("SSH injection detected: %s" % cmd_list) |
177 | 2eba2338 | Stratos Psomadakis | |
178 | 2eba2338 | Stratos Psomadakis | # Second, check whether danger character in command. So the shell
|
179 | 2eba2338 | Stratos Psomadakis | # special operator must be a single argument.
|
180 | 2eba2338 | Stratos Psomadakis | for char in ssh_injection_pattern: |
181 | 2eba2338 | Stratos Psomadakis | if arg == char:
|
182 | 2eba2338 | Stratos Psomadakis | continue
|
183 | 2eba2338 | Stratos Psomadakis | |
184 | 2eba2338 | Stratos Psomadakis | result = arg.find(char) |
185 | 2eba2338 | Stratos Psomadakis | if not result == -1: |
186 | 2eba2338 | Stratos Psomadakis | if result == 0 or not arg[result - 1] == '\\': |
187 | 2eba2338 | Stratos Psomadakis | raise SVCError("SSH injection detected: %s" % cmd_list) |
188 | 2eba2338 | Stratos Psomadakis | |
189 | 2eba2338 | Stratos Psomadakis | def _create_connection(self): |
190 | 2eba2338 | Stratos Psomadakis | """ Create a paramiko SSH connection to the SVC.
|
191 | 2eba2338 | Stratos Psomadakis |
|
192 | 2eba2338 | Stratos Psomadakis | """
|
193 | 2eba2338 | Stratos Psomadakis | self._conn = paramiko.SSHClient()
|
194 | 2eba2338 | Stratos Psomadakis | self._conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
195 | 2eba2338 | Stratos Psomadakis | self._conn.connect(self._host, port=self._port, |
196 | 2eba2338 | Stratos Psomadakis | username=self._user,
|
197 | 2eba2338 | Stratos Psomadakis | password=self._password,
|
198 | 2eba2338 | Stratos Psomadakis | timeout=SVC_SSH_TIMEOUT, |
199 | 2eba2338 | Stratos Psomadakis | look_for_keys=False,
|
200 | 2eba2338 | Stratos Psomadakis | allow_agent=False)
|
201 | 2eba2338 | Stratos Psomadakis | |
202 | 2eba2338 | Stratos Psomadakis | transport = self._conn.get_transport()
|
203 | 2eba2338 | Stratos Psomadakis | transport.sock.settimeout(None)
|
204 | 2eba2338 | Stratos Psomadakis | transport.set_keepalive(SVC_SSH_TIMEOUT) |
205 | 2eba2338 | Stratos Psomadakis | |
206 | 2eba2338 | Stratos Psomadakis | def run_ssh_cmd(self, cmdlist): |
207 | 2eba2338 | Stratos Psomadakis | """ Connect to host and run the command specified in cmdlist.
|
208 | 2eba2338 | Stratos Psomadakis |
|
209 | 2eba2338 | Stratos Psomadakis | @type cmd_list: list of strings
|
210 | 2eba2338 | Stratos Psomadakis | @param cmd_list: a list containing the command to be executed plus any
|
211 | 2eba2338 | Stratos Psomadakis | arguments to the command
|
212 | 2eba2338 | Stratos Psomadakis |
|
213 | 2eba2338 | Stratos Psomadakis | """
|
214 | 2eba2338 | Stratos Psomadakis | self._check_ssh_injection(cmdlist)
|
215 | 2eba2338 | Stratos Psomadakis | |
216 | 2eba2338 | Stratos Psomadakis | cmd = ' '.join(cmdlist)
|
217 | 2eba2338 | Stratos Psomadakis | |
218 | 2eba2338 | Stratos Psomadakis | try:
|
219 | 2eba2338 | Stratos Psomadakis | if self._conn: |
220 | 2eba2338 | Stratos Psomadakis | if not self._conn.get_transport().is_active(): |
221 | 2eba2338 | Stratos Psomadakis | self._conn.close()
|
222 | 2eba2338 | Stratos Psomadakis | self._create_connection()
|
223 | 2eba2338 | Stratos Psomadakis | else:
|
224 | 2eba2338 | Stratos Psomadakis | self._create_connection()
|
225 | 2eba2338 | Stratos Psomadakis | except Exception as exc: |
226 | 2eba2338 | Stratos Psomadakis | raise SVCError("%sCannot create SSH connection to SVC: %s" |
227 | 2eba2338 | Stratos Psomadakis | % (traceback.format_exc(), exc)) |
228 | 2eba2338 | Stratos Psomadakis | |
229 | 2eba2338 | Stratos Psomadakis | conn = self._conn
|
230 | 2eba2338 | Stratos Psomadakis | stderr = ''
|
231 | 2eba2338 | Stratos Psomadakis | |
232 | 2eba2338 | Stratos Psomadakis | try:
|
233 | 2eba2338 | Stratos Psomadakis | stdin_stream, stdout_stream, stderr_stream = conn.exec_command(cmd) |
234 | 2eba2338 | Stratos Psomadakis | channel = stdout_stream.channel |
235 | 2eba2338 | Stratos Psomadakis | |
236 | 2eba2338 | Stratos Psomadakis | # NOTE(justinsb): This seems suspicious...
|
237 | 2eba2338 | Stratos Psomadakis | # ...other SSH clients have buffering issues with this approach
|
238 | 2eba2338 | Stratos Psomadakis | stdout = stdout_stream.read() |
239 | 2eba2338 | Stratos Psomadakis | stderr = stderr_stream.read() |
240 | 2eba2338 | Stratos Psomadakis | stdin_stream.close() |
241 | 2eba2338 | Stratos Psomadakis | |
242 | 2eba2338 | Stratos Psomadakis | exit_status = channel.recv_exit_status() |
243 | 2eba2338 | Stratos Psomadakis | |
244 | 2eba2338 | Stratos Psomadakis | if exit_status != 0: |
245 | 2eba2338 | Stratos Psomadakis | raise Exception("Non-zero exit status %s" % exit_status) |
246 | 2eba2338 | Stratos Psomadakis | |
247 | 2eba2338 | Stratos Psomadakis | return (stdout, stderr)
|
248 | 2eba2338 | Stratos Psomadakis | except Exception as exc: |
249 | 2eba2338 | Stratos Psomadakis | raise SVCError("%sCannot run SSH command '%s' on SVC :%s (%s)" % |
250 | 2eba2338 | Stratos Psomadakis | (traceback.format_exc(), cmd, stderr, exc)) |
251 | 2eba2338 | Stratos Psomadakis | |
252 | 2eba2338 | Stratos Psomadakis | |
253 | 2eba2338 | Stratos Psomadakis | class SVCProvider(object): |
254 | 2eba2338 | Stratos Psomadakis | """ SVC provider class.
|
255 | 2eba2338 | Stratos Psomadakis |
|
256 | 2eba2338 | Stratos Psomadakis | """
|
257 | 2eba2338 | Stratos Psomadakis | |
258 | 2eba2338 | Stratos Psomadakis | def __init__(self, config_file): |
259 | 2eba2338 | Stratos Psomadakis | """ Initializes the SVC provider.
|
260 | 2eba2338 | Stratos Psomadakis |
|
261 | 2eba2338 | Stratos Psomadakis | The method will read env variables and the SVC ini config file to fill
|
262 | 2eba2338 | Stratos Psomadakis | in all the needed info for the SVC provider ops and initialize the
|
263 | 2eba2338 | Stratos Psomadakis | SSH connection to the SVC (L{SVCConnection}).
|
264 | 2eba2338 | Stratos Psomadakis |
|
265 | 2eba2338 | Stratos Psomadakis | @type config_file: string
|
266 | 2eba2338 | Stratos Psomadakis | @param config_file: path to the SVC INI configuration file
|
267 | 2eba2338 | Stratos Psomadakis |
|
268 | 2eba2338 | Stratos Psomadakis | """
|
269 | 2eba2338 | Stratos Psomadakis | super(SVCProvider, self).__init__() |
270 | 2eba2338 | Stratos Psomadakis | |
271 | 2eba2338 | Stratos Psomadakis | self._svc_pool = None |
272 | 2eba2338 | Stratos Psomadakis | |
273 | 2eba2338 | Stratos Psomadakis | self._host_id = None |
274 | 2eba2338 | Stratos Psomadakis | |
275 | 2eba2338 | Stratos Psomadakis | self._vol_name = None |
276 | 2eba2338 | Stratos Psomadakis | self._vol_size = None |
277 | 2eba2338 | Stratos Psomadakis | |
278 | 2eba2338 | Stratos Psomadakis | (host, port, user, password) = self._get_params(config_file)
|
279 | 2eba2338 | Stratos Psomadakis | |
280 | 2eba2338 | Stratos Psomadakis | self._svc_connection = SVCConnection(host, port, user, password)
|
281 | 2eba2338 | Stratos Psomadakis | |
282 | 2eba2338 | Stratos Psomadakis | def _parse_svc_config(self, config_file, san): |
283 | 2eba2338 | Stratos Psomadakis | """"Parse the SVC (Ganeti) configuration file.
|
284 | 2eba2338 | Stratos Psomadakis |
|
285 | 2eba2338 | Stratos Psomadakis | The config should be in 'INI' format.
|
286 | 2eba2338 | Stratos Psomadakis |
|
287 | 2eba2338 | Stratos Psomadakis | @type config_file: string
|
288 | 2eba2338 | Stratos Psomadakis | @param config_file: path to the INI config file
|
289 | 2eba2338 | Stratos Psomadakis | @type san: string
|
290 | 2eba2338 | Stratos Psomadakis | @param san: SVC SAN identifier / section in the INI file
|
291 | 2eba2338 | Stratos Psomadakis | @rtype: list of the SVC config file options
|
292 | 2eba2338 | Stratos Psomadakis | @return: list of the SVC config file options
|
293 | 2eba2338 | Stratos Psomadakis |
|
294 | 2eba2338 | Stratos Psomadakis | """
|
295 | 2eba2338 | Stratos Psomadakis | config = ConfigParser.SafeConfigParser() |
296 | 2eba2338 | Stratos Psomadakis | |
297 | 2eba2338 | Stratos Psomadakis | try:
|
298 | 2eba2338 | Stratos Psomadakis | if not config.read(config_file): |
299 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error("Unable to read config file") |
300 | 2eba2338 | Stratos Psomadakis | |
301 | 2eba2338 | Stratos Psomadakis | expected_opts = frozenset(['host', 'port', 'user', 'password', |
302 | 2eba2338 | Stratos Psomadakis | 'pool', 'host_id']) |
303 | 2eba2338 | Stratos Psomadakis | config_opts = config.options(san) |
304 | 2eba2338 | Stratos Psomadakis | |
305 | 2eba2338 | Stratos Psomadakis | if frozenset(config_opts) != expected_opts: |
306 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Malformed config file') |
307 | 2eba2338 | Stratos Psomadakis | |
308 | 2eba2338 | Stratos Psomadakis | opts = [] |
309 | 2eba2338 | Stratos Psomadakis | |
310 | 2eba2338 | Stratos Psomadakis | host = config.get(san, 'host')
|
311 | 2eba2338 | Stratos Psomadakis | if host == '': |
312 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for host') |
313 | 2eba2338 | Stratos Psomadakis | opts.append(host) |
314 | 2eba2338 | Stratos Psomadakis | |
315 | 2eba2338 | Stratos Psomadakis | port = config.get(san, 'port')
|
316 | 2eba2338 | Stratos Psomadakis | try:
|
317 | 2eba2338 | Stratos Psomadakis | port = int(port)
|
318 | 2eba2338 | Stratos Psomadakis | if port <= 0 or port > 65535: |
319 | 2eba2338 | Stratos Psomadakis | raise ValueError |
320 | 2eba2338 | Stratos Psomadakis | except ValueError: |
321 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for port') |
322 | 2eba2338 | Stratos Psomadakis | opts.append(port) |
323 | 2eba2338 | Stratos Psomadakis | |
324 | 2eba2338 | Stratos Psomadakis | user = config.get(san, 'user')
|
325 | 2eba2338 | Stratos Psomadakis | if user == '': |
326 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for user') |
327 | 2eba2338 | Stratos Psomadakis | opts.append(user) |
328 | 2eba2338 | Stratos Psomadakis | |
329 | 2eba2338 | Stratos Psomadakis | password = config.get(san, 'password')
|
330 | 2eba2338 | Stratos Psomadakis | if password == '': |
331 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for password') |
332 | 2eba2338 | Stratos Psomadakis | opts.append(password) |
333 | 2eba2338 | Stratos Psomadakis | |
334 | 2eba2338 | Stratos Psomadakis | pool = config.get(san, 'pool')
|
335 | 2eba2338 | Stratos Psomadakis | if pool == '': |
336 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for pool') |
337 | 2eba2338 | Stratos Psomadakis | self._svc_pool = pool
|
338 | 2eba2338 | Stratos Psomadakis | |
339 | 2eba2338 | Stratos Psomadakis | host_id = config.get(san, 'host_id')
|
340 | 2eba2338 | Stratos Psomadakis | try:
|
341 | 2eba2338 | Stratos Psomadakis | host_id = int(host_id)
|
342 | 2eba2338 | Stratos Psomadakis | if host_id <= 0: |
343 | 2eba2338 | Stratos Psomadakis | raise ValueError |
344 | 2eba2338 | Stratos Psomadakis | except ValueError: |
345 | 2eba2338 | Stratos Psomadakis | raise ConfigParser.Error('Bad value for host_id') |
346 | 2eba2338 | Stratos Psomadakis | self._host_id = host_id
|
347 | 2eba2338 | Stratos Psomadakis | except ConfigParser.Error as config_error: |
348 | 2eba2338 | Stratos Psomadakis | raise SVCError('Error while reading svc config file: %s.\n' % |
349 | 2eba2338 | Stratos Psomadakis | config_error) |
350 | 2eba2338 | Stratos Psomadakis | |
351 | 2eba2338 | Stratos Psomadakis | return opts
|
352 | 2eba2338 | Stratos Psomadakis | |
353 | 2eba2338 | Stratos Psomadakis | def _get_params(self, config_file): |
354 | 2eba2338 | Stratos Psomadakis | """ Read env variables and the INI config file.
|
355 | 2eba2338 | Stratos Psomadakis |
|
356 | 2eba2338 | Stratos Psomadakis | Return the values of the environmental variables, set by Ganeti, and
|
357 | 2eba2338 | Stratos Psomadakis | the SVC configuration parameters set in the SVC (Ganeti) config file.
|
358 | 2eba2338 | Stratos Psomadakis |
|
359 | 2eba2338 | Stratos Psomadakis | @type config_file: string
|
360 | 2eba2338 | Stratos Psomadakis | @param config_file: path to the INI config file
|
361 | 2eba2338 | Stratos Psomadakis | @rtype: list of the SVC config file options
|
362 | 2eba2338 | Stratos Psomadakis | @return: list of the SVC config file options
|
363 | 2eba2338 | Stratos Psomadakis |
|
364 | 2eba2338 | Stratos Psomadakis | """
|
365 | 2eba2338 | Stratos Psomadakis | |
366 | 2eba2338 | Stratos Psomadakis | self._vol_name = os.getenv("VOL_NAME") |
367 | 2eba2338 | Stratos Psomadakis | if self._vol_name is None: |
368 | 2eba2338 | Stratos Psomadakis | raise SVCError('The environment variable VOL_NAME is missing.\n') |
369 | 2eba2338 | Stratos Psomadakis | |
370 | 2eba2338 | Stratos Psomadakis | if self._vol_name == '': |
371 | 2eba2338 | Stratos Psomadakis | raise SVCError('Volume name cannot be empty.\n') |
372 | 2eba2338 | Stratos Psomadakis | |
373 | 2eba2338 | Stratos Psomadakis | self._vol_size = os.getenv("VOL_SIZE") |
374 | 2eba2338 | Stratos Psomadakis | if self._vol_size is not None: |
375 | 2eba2338 | Stratos Psomadakis | try:
|
376 | 2eba2338 | Stratos Psomadakis | self._vol_size = int(self._vol_size) |
377 | 2eba2338 | Stratos Psomadakis | if self._vol_size <= 0: |
378 | 2eba2338 | Stratos Psomadakis | raise ValueError |
379 | 2eba2338 | Stratos Psomadakis | except ValueError: |
380 | 2eba2338 | Stratos Psomadakis | raise SVCError('Volume size must be positive integer') |
381 | 2eba2338 | Stratos Psomadakis | |
382 | 2eba2338 | Stratos Psomadakis | san = os.getenv("EXTP_SVC_SAN")
|
383 | 2eba2338 | Stratos Psomadakis | if san is None: |
384 | 2eba2338 | Stratos Psomadakis | raise SVCError('The environment variable EXTP_SVC_SAN is' |
385 | 2eba2338 | Stratos Psomadakis | 'missing.\n')
|
386 | 2eba2338 | Stratos Psomadakis | if san == '': |
387 | 2eba2338 | Stratos Psomadakis | raise SVCError('EXTP_SVC_SAN cannot be none') |
388 | 2eba2338 | Stratos Psomadakis | |
389 | 2eba2338 | Stratos Psomadakis | return self._parse_svc_config(config_file, san) |
390 | 2eba2338 | Stratos Psomadakis | |
391 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
392 | 2eba2338 | Stratos Psomadakis | def _driver_assert(assert_condition, exception_message): |
393 | 2eba2338 | Stratos Psomadakis | """ Internal assertion mechanism for CLI output.
|
394 | 2eba2338 | Stratos Psomadakis |
|
395 | 2eba2338 | Stratos Psomadakis | @type assert_condition: boolean
|
396 | 2eba2338 | Stratos Psomadakis | @param assert_condition: condition to assert against
|
397 | 2eba2338 | Stratos Psomadakis | @type exception_message: string
|
398 | 2eba2338 | Stratos Psomadakis | @param exception_message: exception message to print
|
399 | 2eba2338 | Stratos Psomadakis |
|
400 | 2eba2338 | Stratos Psomadakis | """
|
401 | 2eba2338 | Stratos Psomadakis | if not assert_condition: |
402 | 2eba2338 | Stratos Psomadakis | raise SVCError(exception_message)
|
403 | 2eba2338 | Stratos Psomadakis | |
404 | 2eba2338 | Stratos Psomadakis | def _assert_ssh_return(self, test, fun, ssh_cmd, out, err): |
405 | 2eba2338 | Stratos Psomadakis | """ Call internal assertion mechanism for SSH cmds.
|
406 | 2eba2338 | Stratos Psomadakis |
|
407 | 2eba2338 | Stratos Psomadakis | @type test: boolean
|
408 | 2eba2338 | Stratos Psomadakis | @param test: condition to assert against
|
409 | 2eba2338 | Stratos Psomadakis | @type fun: object
|
410 | 2eba2338 | Stratos Psomadakis | @param fun: function / method which called us
|
411 | 2eba2338 | Stratos Psomadakis | @type ssh_cmd: list of strings
|
412 | 2eba2338 | Stratos Psomadakis | @param ssh_cmd: list of strings (command plus command arguments)
|
413 | 2eba2338 | Stratos Psomadakis | @type out: buffer
|
414 | 2eba2338 | Stratos Psomadakis | @param out: stdout buffer
|
415 | 2eba2338 | Stratos Psomadakis | @type err: buffer
|
416 | 2eba2338 | Stratos Psomadakis | @param err: stderr buffer
|
417 | 2eba2338 | Stratos Psomadakis |
|
418 | 2eba2338 | Stratos Psomadakis | """
|
419 | 2eba2338 | Stratos Psomadakis | self._driver_assert(
|
420 | 2eba2338 | Stratos Psomadakis | test, |
421 | 2eba2338 | Stratos Psomadakis | '%(fun)s: Failed with unexpected CLI output.\n '
|
422 | 2eba2338 | Stratos Psomadakis | 'Command: %(cmd)s\n stdout: %(out)s\n stderr: %(err)s'
|
423 | 2eba2338 | Stratos Psomadakis | % {'fun': fun,
|
424 | 2eba2338 | Stratos Psomadakis | 'cmd': ssh_cmd,
|
425 | 2eba2338 | Stratos Psomadakis | 'out': str(out), |
426 | 2eba2338 | Stratos Psomadakis | 'err': str(err)}) |
427 | 2eba2338 | Stratos Psomadakis | |
428 | 2eba2338 | Stratos Psomadakis | def _exec_cmd_and_parse_attrs(self, ssh_cmd): |
429 | 2eba2338 | Stratos Psomadakis | """Execute command on the Storwize/SVC and parse attributes.
|
430 | 2eba2338 | Stratos Psomadakis |
|
431 | 2eba2338 | Stratos Psomadakis | Exception is raised if the information from the system
|
432 | 2eba2338 | Stratos Psomadakis | can not be obtained.
|
433 | 2eba2338 | Stratos Psomadakis |
|
434 | 2eba2338 | Stratos Psomadakis | @type ssh_cmd: list of strings
|
435 | 2eba2338 | Stratos Psomadakis | @param ssh_cmd: a list of the command and additional arguments
|
436 | 2eba2338 | Stratos Psomadakis | @rtype: dict
|
437 | 2eba2338 | Stratos Psomadakis | @return: a dictionary containing the attributes returned by ssh cmd
|
438 | 2eba2338 | Stratos Psomadakis |
|
439 | 2eba2338 | Stratos Psomadakis | """
|
440 | 2eba2338 | Stratos Psomadakis | |
441 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
442 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out), |
443 | 2eba2338 | Stratos Psomadakis | '_exec_cmd_and_parse_attrs',
|
444 | 2eba2338 | Stratos Psomadakis | ssh_cmd, out, err) |
445 | 2eba2338 | Stratos Psomadakis | |
446 | 2eba2338 | Stratos Psomadakis | attributes = {} |
447 | 2eba2338 | Stratos Psomadakis | for attrib_line in out.split('\n'): |
448 | 2eba2338 | Stratos Psomadakis | # If '!' not found, return the string and two empty strings
|
449 | 2eba2338 | Stratos Psomadakis | attrib_name, _, attrib_value = attrib_line.partition('!')
|
450 | 2eba2338 | Stratos Psomadakis | if attrib_name is not None and len(attrib_name.strip()): |
451 | 2eba2338 | Stratos Psomadakis | attributes[attrib_name] = attrib_value |
452 | 2eba2338 | Stratos Psomadakis | |
453 | 2eba2338 | Stratos Psomadakis | return attributes
|
454 | 2eba2338 | Stratos Psomadakis | |
455 | 2eba2338 | Stratos Psomadakis | def _get_hdr_dic(self, header, row, delim): |
456 | 2eba2338 | Stratos Psomadakis | """Return CLI row data as a dictionary indexed by names from header.
|
457 | 2eba2338 | Stratos Psomadakis |
|
458 | 2eba2338 | Stratos Psomadakis | The strings are converted to columns using the delimiter in
|
459 | 2eba2338 | Stratos Psomadakis | delim.
|
460 | 2eba2338 | Stratos Psomadakis |
|
461 | 2eba2338 | Stratos Psomadakis | @type header: string
|
462 | 2eba2338 | Stratos Psomadakis | @param header: header string
|
463 | 2eba2338 | Stratos Psomadakis | @type row: string
|
464 | 2eba2338 | Stratos Psomadakis | @param row: row string
|
465 | 2eba2338 | Stratos Psomadakis | @type delim: string
|
466 | 2eba2338 | Stratos Psomadakis | @param delim: column delimiter
|
467 | 2eba2338 | Stratos Psomadakis | @rtype: dict
|
468 | 2eba2338 | Stratos Psomadakis | @return: dictionary containing CLI row data
|
469 | 2eba2338 | Stratos Psomadakis |
|
470 | 2eba2338 | Stratos Psomadakis | """
|
471 | 2eba2338 | Stratos Psomadakis | |
472 | 2eba2338 | Stratos Psomadakis | attributes = header.split(delim) |
473 | 2eba2338 | Stratos Psomadakis | values = row.split(delim) |
474 | 2eba2338 | Stratos Psomadakis | |
475 | 2eba2338 | Stratos Psomadakis | self._driver_assert(
|
476 | 2eba2338 | Stratos Psomadakis | len(values) ==
|
477 | 2eba2338 | Stratos Psomadakis | len(attributes),
|
478 | 2eba2338 | Stratos Psomadakis | '_get_hdr_dic: attribute headers and values do not match.\n '
|
479 | 2eba2338 | Stratos Psomadakis | 'Headers: %(header)s\n Values: %(row)s'
|
480 | 2eba2338 | Stratos Psomadakis | % {'header': str(header), |
481 | 2eba2338 | Stratos Psomadakis | 'row': str(row)}) |
482 | 2eba2338 | Stratos Psomadakis | |
483 | 2eba2338 | Stratos Psomadakis | # Is it the same as dict(zip())?
|
484 | 2eba2338 | Stratos Psomadakis | #dic = dict((a, v) for a, v in map(None, attributes, values))
|
485 | 2eba2338 | Stratos Psomadakis | return dict(zip(attributes, values)) |
486 | 2eba2338 | Stratos Psomadakis | |
487 | 2eba2338 | Stratos Psomadakis | def _get_hostvdisk_mappings(self, host_id): |
488 | 2eba2338 | Stratos Psomadakis | """ Return the defined storage mappings for a host.
|
489 | 2eba2338 | Stratos Psomadakis |
|
490 | 2eba2338 | Stratos Psomadakis | @type host_id: integer
|
491 | 2eba2338 | Stratos Psomadakis | @param host_id: id of the host in the SVC
|
492 | 2eba2338 | Stratos Psomadakis | @rtype: dict
|
493 | 2eba2338 | Stratos Psomadakis | @return: dictionary containing the storage mappings for a host
|
494 | 2eba2338 | Stratos Psomadakis |
|
495 | 2eba2338 | Stratos Psomadakis | """
|
496 | 2eba2338 | Stratos Psomadakis | |
497 | 2eba2338 | Stratos Psomadakis | return_data = {} |
498 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svcinfo', 'lshostvdiskmap', '-delim', '!', str(host_id)] |
499 | 2eba2338 | Stratos Psomadakis | out, _ = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
500 | 2eba2338 | Stratos Psomadakis | |
501 | 2eba2338 | Stratos Psomadakis | mappings = out.strip().split('\n')
|
502 | 2eba2338 | Stratos Psomadakis | if len(mappings): |
503 | 2eba2338 | Stratos Psomadakis | header = mappings.pop(0)
|
504 | 2eba2338 | Stratos Psomadakis | for mapping_line in mappings: |
505 | 2eba2338 | Stratos Psomadakis | mapping_data = self._get_hdr_dic(header, mapping_line, '!') |
506 | 2eba2338 | Stratos Psomadakis | return_data[mapping_data['vdisk_name']] = mapping_data
|
507 | 2eba2338 | Stratos Psomadakis | |
508 | 2eba2338 | Stratos Psomadakis | return return_data
|
509 | 2eba2338 | Stratos Psomadakis | |
510 | 2eba2338 | Stratos Psomadakis | def _map_vol_to_host(self, volume_name, host_id): |
511 | 2eba2338 | Stratos Psomadakis | """ Create a mapping between a volume to a host.
|
512 | 2eba2338 | Stratos Psomadakis |
|
513 | 2eba2338 | Stratos Psomadakis | @type volume_name: string
|
514 | 2eba2338 | Stratos Psomadakis | @param volume_name: volume name
|
515 | 2eba2338 | Stratos Psomadakis | @type host_id: integer
|
516 | 2eba2338 | Stratos Psomadakis | @param host_id: id of the host in the SVC
|
517 | 2eba2338 | Stratos Psomadakis |
|
518 | 2eba2338 | Stratos Psomadakis | """
|
519 | 2eba2338 | Stratos Psomadakis | |
520 | 2eba2338 | Stratos Psomadakis | assert(host_id in _SVC_VALID_RANGE) |
521 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svctask', 'mkvdiskhostmap', '-host', str(host_id), |
522 | 2eba2338 | Stratos Psomadakis | volume_name] |
523 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
524 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return('successfully created' in out, |
525 | 2eba2338 | Stratos Psomadakis | '_map_vol_to_host', ssh_cmd, out, err)
|
526 | 2eba2338 | Stratos Psomadakis | |
527 | 2eba2338 | Stratos Psomadakis | def _unmap_vol_from_host(self, volume_name, host_id): |
528 | 2eba2338 | Stratos Psomadakis | """ Delete a mapping between a volume and a host.
|
529 | 2eba2338 | Stratos Psomadakis |
|
530 | 2eba2338 | Stratos Psomadakis | @type volume_name: string
|
531 | 2eba2338 | Stratos Psomadakis | @param volume_name: volume name
|
532 | 2eba2338 | Stratos Psomadakis | @type host_id: integer
|
533 | 2eba2338 | Stratos Psomadakis | @param host_id: id of the host in the SVC
|
534 | 2eba2338 | Stratos Psomadakis |
|
535 | 2eba2338 | Stratos Psomadakis | """
|
536 | 2eba2338 | Stratos Psomadakis | assert(host_id in _SVC_VALID_RANGE) |
537 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svctask', 'rmvdiskhostmap', '-host', str(host_id), |
538 | 2eba2338 | Stratos Psomadakis | volume_name] |
539 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
540 | 2eba2338 | Stratos Psomadakis | # Verify CLI behaviour - no output is returned from
|
541 | 2eba2338 | Stratos Psomadakis | # rmvdiskhostmap
|
542 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out.strip()) == 0, |
543 | 2eba2338 | Stratos Psomadakis | 'unmap_vol_from_host', ssh_cmd, out, err)
|
544 | 2eba2338 | Stratos Psomadakis | |
545 | 2eba2338 | Stratos Psomadakis | def _get_vdisk_attributes(self, vdisk_name): |
546 | 2eba2338 | Stratos Psomadakis | """ Return vdisk attributes, or None if vdisk does not exist.
|
547 | 2eba2338 | Stratos Psomadakis |
|
548 | 2eba2338 | Stratos Psomadakis | Exception is raised if the information from system can not be
|
549 | 2eba2338 | Stratos Psomadakis | parsed/matched to a single vdisk.
|
550 | 2eba2338 | Stratos Psomadakis |
|
551 | 2eba2338 | Stratos Psomadakis | @type vdisk_name: string
|
552 | 2eba2338 | Stratos Psomadakis | @param vdisk_name: vdisk name
|
553 | 2eba2338 | Stratos Psomadakis | @rtype: dict
|
554 | 2eba2338 | Stratos Psomadakis | @return: a dictionary containing the vdisk attributes
|
555 | 2eba2338 | Stratos Psomadakis |
|
556 | 2eba2338 | Stratos Psomadakis | """
|
557 | 2eba2338 | Stratos Psomadakis | |
558 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svcinfo', 'lsvdisk', '-bytes', '-delim', '!', vdisk_name] |
559 | 2eba2338 | Stratos Psomadakis | return self._exec_cmd_and_parse_attrs(ssh_cmd) |
560 | 2eba2338 | Stratos Psomadakis | |
561 | 2eba2338 | Stratos Psomadakis | def _is_vdisk_defined(self, vdisk_name): |
562 | 2eba2338 | Stratos Psomadakis | """ Check if vdisk is defined.
|
563 | 2eba2338 | Stratos Psomadakis |
|
564 | 2eba2338 | Stratos Psomadakis | @type vdisk_name: string
|
565 | 2eba2338 | Stratos Psomadakis | @param vdisk_name: vdisk name
|
566 | 2eba2338 | Stratos Psomadakis | @rtype: bool
|
567 | 2eba2338 | Stratos Psomadakis | @return: vdisk defined or not
|
568 | 2eba2338 | Stratos Psomadakis |
|
569 | 2eba2338 | Stratos Psomadakis | """
|
570 | 2eba2338 | Stratos Psomadakis | # Maybe get list of disks and search instead of relying on empty
|
571 | 2eba2338 | Stratos Psomadakis | # attr reply
|
572 | 2eba2338 | Stratos Psomadakis | try:
|
573 | 2eba2338 | Stratos Psomadakis | self._get_vdisk_attributes(vdisk_name)
|
574 | 2eba2338 | Stratos Psomadakis | return True |
575 | 2eba2338 | Stratos Psomadakis | except SVCError:
|
576 | 2eba2338 | Stratos Psomadakis | return False |
577 | 2eba2338 | Stratos Psomadakis | |
578 | 2eba2338 | Stratos Psomadakis | def _is_vdisk_mapped(self, vdisk_name, host_id): |
579 | 2eba2338 | Stratos Psomadakis | """ Check if vdisk is mapped to the host. """
|
580 | 2eba2338 | Stratos Psomadakis | return vdisk_name in self._get_hostvdisk_mappings(host_id) |
581 | 2eba2338 | Stratos Psomadakis | |
582 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
583 | 2eba2338 | Stratos Psomadakis | def _check_call(cmdlist): |
584 | 2eba2338 | Stratos Psomadakis | """ Wrapper around subprocess.check_call.
|
585 | 2eba2338 | Stratos Psomadakis |
|
586 | 2eba2338 | Stratos Psomadakis | @type cmdlist: list of strings
|
587 | 2eba2338 | Stratos Psomadakis | @param cmdlist: list of command plus arguments
|
588 | 2eba2338 | Stratos Psomadakis |
|
589 | 2eba2338 | Stratos Psomadakis | """
|
590 | 2eba2338 | Stratos Psomadakis | try:
|
591 | 2eba2338 | Stratos Psomadakis | subprocess.check_call(cmdlist, stderr=subprocess.STDOUT) |
592 | 2eba2338 | Stratos Psomadakis | except subprocess.CalledProcessError as exc: |
593 | 2eba2338 | Stratos Psomadakis | raise SVCError("Command %s failed: %s" % (' '.join(cmdlist), |
594 | 2eba2338 | Stratos Psomadakis | exc.output)) |
595 | 2eba2338 | Stratos Psomadakis | |
596 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
597 | 2eba2338 | Stratos Psomadakis | def _check_output(cmdlist): |
598 | 2eba2338 | Stratos Psomadakis | """ Wrapper around subprocess.check_output.
|
599 | 2eba2338 | Stratos Psomadakis |
|
600 | 2eba2338 | Stratos Psomadakis | @type cmdlist: list of strings
|
601 | 2eba2338 | Stratos Psomadakis | @param cmdlist: list of command plus arguments
|
602 | 2eba2338 | Stratos Psomadakis | @rtype: string
|
603 | 2eba2338 | Stratos Psomadakis | @return: output of cmdlist
|
604 | 2eba2338 | Stratos Psomadakis |
|
605 | 2eba2338 | Stratos Psomadakis | """
|
606 | 2eba2338 | Stratos Psomadakis | try:
|
607 | 2eba2338 | Stratos Psomadakis | return subprocess.check_output(cmdlist, stderr=subprocess.STDOUT)
|
608 | 2eba2338 | Stratos Psomadakis | except subprocess.CalledProcessError as exc: |
609 | 2eba2338 | Stratos Psomadakis | raise SVCError("Command %s failed: %s" % (' '.join(cmdlist), |
610 | 2eba2338 | Stratos Psomadakis | exc.output)) |
611 | 2eba2338 | Stratos Psomadakis | |
612 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
613 | 2eba2338 | Stratos Psomadakis | def _get_devices(lun): |
614 | 2eba2338 | Stratos Psomadakis | """ Get the devices mapped to host, corresponding to LUN id lun
|
615 | 2eba2338 | Stratos Psomadakis |
|
616 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
617 | 2eba2338 | Stratos Psomadakis | @param lun: LUN id
|
618 | 2eba2338 | Stratos Psomadakis | @rtype: list of strings
|
619 | 2eba2338 | Stratos Psomadakis | @return: devices discovered by host or not
|
620 | 2eba2338 | Stratos Psomadakis |
|
621 | 2eba2338 | Stratos Psomadakis | """
|
622 | 2eba2338 | Stratos Psomadakis | return glob.glob("%s/*lun%s" % (_DEV_BYPATH, lun)) |
623 | 2eba2338 | Stratos Psomadakis | |
624 | 2eba2338 | Stratos Psomadakis | def _devices_discovered(self, lun): |
625 | 2eba2338 | Stratos Psomadakis | """ Make sure the devices get created on the host.
|
626 | 2eba2338 | Stratos Psomadakis |
|
627 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
628 | 2eba2338 | Stratos Psomadakis | @param lun: LUN id
|
629 | 2eba2338 | Stratos Psomadakis | @rtype: bool
|
630 | 2eba2338 | Stratos Psomadakis | @return: devices discovered by host or not
|
631 | 2eba2338 | Stratos Psomadakis |
|
632 | 2eba2338 | Stratos Psomadakis | """
|
633 | 2eba2338 | Stratos Psomadakis | return len(self._get_devices(lun)) == 4 |
634 | 2eba2338 | Stratos Psomadakis | |
635 | 2eba2338 | Stratos Psomadakis | def _devices_add(self, lun): |
636 | 2eba2338 | Stratos Psomadakis | """ Rescan SCSI / FC bus for new devices.
|
637 | 2eba2338 | Stratos Psomadakis |
|
638 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
639 | 2eba2338 | Stratos Psomadakis | @param lun: LUN id
|
640 | 2eba2338 | Stratos Psomadakis |
|
641 | 2eba2338 | Stratos Psomadakis | """
|
642 | 2eba2338 | Stratos Psomadakis | self._check_call([_RESCAN_SCSI_CMD])
|
643 | 2eba2338 | Stratos Psomadakis | if self._devices_discovered(lun) is False: |
644 | 2eba2338 | Stratos Psomadakis | raise SVCError("SCSI bus rescan failed to properly add the new " |
645 | 2eba2338 | Stratos Psomadakis | "devices")
|
646 | 2eba2338 | Stratos Psomadakis | |
647 | 2eba2338 | Stratos Psomadakis | def _devices_remove(self, lun): |
648 | 2eba2338 | Stratos Psomadakis | """ Remove scanned devices.
|
649 | 2eba2338 | Stratos Psomadakis |
|
650 | 2eba2338 | Stratos Psomadakis | @type device: string
|
651 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
652 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
653 | 2eba2338 | Stratos Psomadakis | @param lun: LUN id
|
654 | 2eba2338 | Stratos Psomadakis |
|
655 | 2eba2338 | Stratos Psomadakis | """
|
656 | 2eba2338 | Stratos Psomadakis | # Which one is better?:
|
657 | 2eba2338 | Stratos Psomadakis | #dm_device = os.path.split(os.readlink(device))[-1]
|
658 | 2eba2338 | Stratos Psomadakis | #dm_slaves = os.listdir(_SYSFS_SLAVES_PATH % {'blockdev': dm_device})
|
659 | 2eba2338 | Stratos Psomadakis | dm_slaves = self._get_devices(lun)
|
660 | 2eba2338 | Stratos Psomadakis | |
661 | 2eba2338 | Stratos Psomadakis | # Delete the disks
|
662 | 2eba2338 | Stratos Psomadakis | try:
|
663 | 2eba2338 | Stratos Psomadakis | for slave in dm_slaves: |
664 | 2eba2338 | Stratos Psomadakis | delete_path = _SYSFS_FCDELETE_PATH % {'blockdev': slave}
|
665 | 2eba2338 | Stratos Psomadakis | with open(delete_path, 'w') as delete_bdev_file: |
666 | 2eba2338 | Stratos Psomadakis | delete_bdev_file.write('1')
|
667 | 2eba2338 | Stratos Psomadakis | except IOError as exc: |
668 | 2eba2338 | Stratos Psomadakis | raise SVCError("Cannot delete FC devices: %s" % exc) |
669 | 2eba2338 | Stratos Psomadakis | |
670 | 2eba2338 | Stratos Psomadakis | if self._devices_discovered(lun) is True: |
671 | 2eba2338 | Stratos Psomadakis | raise SVCError('Failed to remove FC devices') |
672 | 2eba2338 | Stratos Psomadakis | |
673 | 2eba2338 | Stratos Psomadakis | def _multipath_ok(self, device): |
674 | 2eba2338 | Stratos Psomadakis | """ Check if multipath device is set up correctly.
|
675 | 2eba2338 | Stratos Psomadakis |
|
676 | 2eba2338 | Stratos Psomadakis | @type device: string
|
677 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
678 | 2eba2338 | Stratos Psomadakis | @rtype: bool
|
679 | 2eba2338 | Stratos Psomadakis | @return: multipath device ok or not
|
680 | 2eba2338 | Stratos Psomadakis |
|
681 | 2eba2338 | Stratos Psomadakis | """
|
682 | 2eba2338 | Stratos Psomadakis | self._check_call([_DMSETUP_CMD, 'info', device]) |
683 | 2eba2338 | Stratos Psomadakis | return self._check_output([_MULTIPATH_CMD, '-ll', device]) != "" |
684 | 2eba2338 | Stratos Psomadakis | |
685 | 2eba2338 | Stratos Psomadakis | def _multipath_setup(self, device): |
686 | 2eba2338 | Stratos Psomadakis | """ Setup multpath / dm for newly discovered devices.
|
687 | 2eba2338 | Stratos Psomadakis |
|
688 | 2eba2338 | Stratos Psomadakis | @type device: string
|
689 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
690 | 2eba2338 | Stratos Psomadakis |
|
691 | 2eba2338 | Stratos Psomadakis | """
|
692 | 2eba2338 | Stratos Psomadakis | self._check_call([_MULTIPATH_CMD])
|
693 | 2eba2338 | Stratos Psomadakis | if self._multipath_ok(device) is False: |
694 | 2eba2338 | Stratos Psomadakis | raise SVCError("Multipath / DM setup failed") |
695 | 2eba2338 | Stratos Psomadakis | |
696 | 2eba2338 | Stratos Psomadakis | def _multipath_remove(self, device): |
697 | 2eba2338 | Stratos Psomadakis | """ Tear down multipath.
|
698 | 2eba2338 | Stratos Psomadakis |
|
699 | 2eba2338 | Stratos Psomadakis | @type device: string
|
700 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
701 | 2eba2338 | Stratos Psomadakis |
|
702 | 2eba2338 | Stratos Psomadakis | """
|
703 | 2eba2338 | Stratos Psomadakis | self._check_call([_MULTIPATH_CMD, '-f', device]) |
704 | 2eba2338 | Stratos Psomadakis | if self._multipath_ok(device) is True: |
705 | 2eba2338 | Stratos Psomadakis | raise SVCError("Unable to reove multipath device") |
706 | 2eba2338 | Stratos Psomadakis | |
707 | 2eba2338 | Stratos Psomadakis | def _host_device_remove(self, device, lun): |
708 | 2eba2338 | Stratos Psomadakis | """ Tear down multipath and remove the scanned devices.
|
709 | 2eba2338 | Stratos Psomadakis |
|
710 | 2eba2338 | Stratos Psomadakis | @type device: string
|
711 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
712 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
713 | 2eba2338 | Stratos Psomadakis | @type lun: LUN id
|
714 | 2eba2338 | Stratos Psomadakis |
|
715 | 2eba2338 | Stratos Psomadakis | """
|
716 | 2eba2338 | Stratos Psomadakis | self._multipath_remove(device)
|
717 | 2eba2338 | Stratos Psomadakis | self._devices_remove(lun)
|
718 | 2eba2338 | Stratos Psomadakis | |
719 | 2eba2338 | Stratos Psomadakis | def _host_device_setup(self, device, lun): |
720 | 2eba2338 | Stratos Psomadakis | """ Discover and setup the devices on the host machine.
|
721 | 2eba2338 | Stratos Psomadakis |
|
722 | 2eba2338 | Stratos Psomadakis | @type device: string
|
723 | 2eba2338 | Stratos Psomadakis | @param device: device string
|
724 | 2eba2338 | Stratos Psomadakis | @type lun: integer
|
725 | 2eba2338 | Stratos Psomadakis | @type lun: LUN id
|
726 | 2eba2338 | Stratos Psomadakis |
|
727 | 2eba2338 | Stratos Psomadakis | """
|
728 | 2eba2338 | Stratos Psomadakis | try:
|
729 | 2eba2338 | Stratos Psomadakis | self._devices_add(lun)
|
730 | 2eba2338 | Stratos Psomadakis | self._multipath_setup(device)
|
731 | 2eba2338 | Stratos Psomadakis | except:
|
732 | 2eba2338 | Stratos Psomadakis | # Cleanup any devices added by SCSI bus rescan
|
733 | 2eba2338 | Stratos Psomadakis | # and remove device mappings
|
734 | 2eba2338 | Stratos Psomadakis | self._host_device_remove(device, lun)
|
735 | 2eba2338 | Stratos Psomadakis | raise
|
736 | 2eba2338 | Stratos Psomadakis | |
737 | 2eba2338 | Stratos Psomadakis | def _get_device_info(self, volume): |
738 | 2eba2338 | Stratos Psomadakis | """ Return device_string and lun id for a volume.
|
739 | 2eba2338 | Stratos Psomadakis |
|
740 | 2eba2338 | Stratos Psomadakis | @type volume: string
|
741 | 2eba2338 | Stratos Psomadakis | @param volume: volume name
|
742 | 2eba2338 | Stratos Psomadakis | @rtype: tuple
|
743 | 2eba2338 | Stratos Psomadakis | @return: tuple containing device string and LUN id
|
744 | 2eba2338 | Stratos Psomadakis |
|
745 | 2eba2338 | Stratos Psomadakis | """
|
746 | 2eba2338 | Stratos Psomadakis | if not self._is_vdisk_defined(volume): |
747 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume not found") |
748 | 2eba2338 | Stratos Psomadakis | |
749 | 2eba2338 | Stratos Psomadakis | volume_attributes = self._get_vdisk_attributes(volume)
|
750 | 2eba2338 | Stratos Psomadakis | |
751 | 2eba2338 | Stratos Psomadakis | try:
|
752 | 2eba2338 | Stratos Psomadakis | vdisk_id = int(volume_attributes['vdisk_UID']) |
753 | 2eba2338 | Stratos Psomadakis | lun_id = int(volume_attributes['SCSI_id']) |
754 | 2eba2338 | Stratos Psomadakis | except (KeyError, ValueError): |
755 | 2eba2338 | Stratos Psomadakis | raise SVCError("Malformed vdisk attributes") |
756 | 2eba2338 | Stratos Psomadakis | |
757 | 2eba2338 | Stratos Psomadakis | device_string = "/dev/mapper/%s%s" % (_SCSI_DEV_PREFIX, vdisk_id)
|
758 | 2eba2338 | Stratos Psomadakis | |
759 | 2eba2338 | Stratos Psomadakis | return (device_string, lun_id)
|
760 | 2eba2338 | Stratos Psomadakis | |
761 | 2eba2338 | Stratos Psomadakis | def attach(self): |
762 | 2eba2338 | Stratos Psomadakis | """ Attach vdisk. """
|
763 | 2eba2338 | Stratos Psomadakis | if self._vol_size is not None: |
764 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume size is set, when it shouldn't") |
765 | 2eba2338 | Stratos Psomadakis | |
766 | 2eba2338 | Stratos Psomadakis | volume_name = self._vol_name
|
767 | 2eba2338 | Stratos Psomadakis | |
768 | 2eba2338 | Stratos Psomadakis | device_string, lun_id = self._get_device_info(volume_name)
|
769 | 2eba2338 | Stratos Psomadakis | |
770 | 2eba2338 | Stratos Psomadakis | check_list = self._verify_checklist()
|
771 | 2eba2338 | Stratos Psomadakis | if self._consistent_unmap(check_list): |
772 | 2eba2338 | Stratos Psomadakis | self._map_vol_to_host(volume_name, self._host_id) |
773 | 2eba2338 | Stratos Psomadakis | self._host_device_setup(device_string, lun_id)
|
774 | 2eba2338 | Stratos Psomadakis | if not self._consistent_map(check_list): |
775 | 2eba2338 | Stratos Psomadakis | raise SVCError("Attach failed") |
776 | 2eba2338 | Stratos Psomadakis | elif not self._consistent_map(check_list): |
777 | 2eba2338 | Stratos Psomadakis | raise SVCError("Incosistent map state") |
778 | 2eba2338 | Stratos Psomadakis | |
779 | 2eba2338 | Stratos Psomadakis | sys.stdout.write('%s' % device_string)
|
780 | 2eba2338 | Stratos Psomadakis | |
781 | 2eba2338 | Stratos Psomadakis | def create(self): |
782 | 2eba2338 | Stratos Psomadakis | """ Create a new vdisk. """
|
783 | 2eba2338 | Stratos Psomadakis | |
784 | 2eba2338 | Stratos Psomadakis | if self._vol_size is None: |
785 | 2eba2338 | Stratos Psomadakis | raise SVCError("Missing volume size for create") |
786 | 2eba2338 | Stratos Psomadakis | |
787 | 2eba2338 | Stratos Psomadakis | if self._is_vdisk_defined(self._vol_name): |
788 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume exists") |
789 | 2eba2338 | Stratos Psomadakis | |
790 | 2eba2338 | Stratos Psomadakis | assert(self._svc_pool == _SVC_POOL_NAME) |
791 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svctask', 'mkvdisk', '-name', self._vol_name, '-mdiskgrp', |
792 | 2eba2338 | Stratos Psomadakis | self._svc_pool, '-size', str(self._vol_size)] |
793 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
794 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out.strip()), '_create_vdisk', |
795 | 2eba2338 | Stratos Psomadakis | ssh_cmd, out, err) |
796 | 2eba2338 | Stratos Psomadakis | |
797 | 2eba2338 | Stratos Psomadakis | # Ensure that the output is as expected
|
798 | 2eba2338 | Stratos Psomadakis | match_obj = re.search(r'Virtual Disk, id \[([0-9]+)\], '
|
799 | 2eba2338 | Stratos Psomadakis | 'successfully created', out)
|
800 | 2eba2338 | Stratos Psomadakis | # Make sure we got a "successfully created" message with vdisk id
|
801 | 2eba2338 | Stratos Psomadakis | self._driver_assert(
|
802 | 2eba2338 | Stratos Psomadakis | match_obj is not None, |
803 | 2eba2338 | Stratos Psomadakis | '_create_vdisk %(name)s - did not find '
|
804 | 2eba2338 | Stratos Psomadakis | 'success message in CLI output.\n '
|
805 | 2eba2338 | Stratos Psomadakis | 'stdout: %(out)s\n stderr: %(err)s'
|
806 | 2eba2338 | Stratos Psomadakis | % {'name': self._vol_name, 'out': str(out), 'err': str(err)}) |
807 | 2eba2338 | Stratos Psomadakis | |
808 | 2eba2338 | Stratos Psomadakis | def detach(self): |
809 | 2eba2338 | Stratos Psomadakis | """ Detach vdisk. """
|
810 | 2eba2338 | Stratos Psomadakis | if self._vol_size is not None: |
811 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume size is set, when it shouldn't") |
812 | 2eba2338 | Stratos Psomadakis | |
813 | 2eba2338 | Stratos Psomadakis | volume_name = self._vol_name
|
814 | 2eba2338 | Stratos Psomadakis | check_list = self._verify_checklist()
|
815 | 2eba2338 | Stratos Psomadakis | |
816 | 2eba2338 | Stratos Psomadakis | if self._consistent_map(check_list): |
817 | 2eba2338 | Stratos Psomadakis | device_string, lun_id = self._get_device_info(volume_name)
|
818 | 2eba2338 | Stratos Psomadakis | |
819 | 2eba2338 | Stratos Psomadakis | self._host_device_remove(device_string, lun_id)
|
820 | 2eba2338 | Stratos Psomadakis | self._unmap_vol_from_host(volume_name, self._host_id) |
821 | 2eba2338 | Stratos Psomadakis | if not self._consistent_unmap(check_list): |
822 | 2eba2338 | Stratos Psomadakis | raise SVCError("Detatch failed") |
823 | 2eba2338 | Stratos Psomadakis | elif not self._consistent_unmap(check_list): |
824 | 2eba2338 | Stratos Psomadakis | raise SVCError("Incosistent map") |
825 | 2eba2338 | Stratos Psomadakis | |
826 | 2eba2338 | Stratos Psomadakis | def remove(self): |
827 | 2eba2338 | Stratos Psomadakis | """ Deletes existing vdisks. """
|
828 | 2eba2338 | Stratos Psomadakis | if self._vol_size is not None: |
829 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume size is set, when it shouldn't") |
830 | 2eba2338 | Stratos Psomadakis | |
831 | 2eba2338 | Stratos Psomadakis | volume_name = self._vol_name
|
832 | 2eba2338 | Stratos Psomadakis | |
833 | 2eba2338 | Stratos Psomadakis | if self._is_vdisk_mapped(volume_name, self._host_id): |
834 | 2eba2338 | Stratos Psomadakis | raise SVCError('Tried to delete mapped volume %s' % volume_name) |
835 | 2eba2338 | Stratos Psomadakis | |
836 | 2eba2338 | Stratos Psomadakis | # Try to delete volume only if found on the storage
|
837 | 2eba2338 | Stratos Psomadakis | vdisk_defined = self._is_vdisk_defined(volume_name)
|
838 | 2eba2338 | Stratos Psomadakis | if not vdisk_defined: |
839 | 2eba2338 | Stratos Psomadakis | raise SVCError('warning: Tried to delete vdisk %s but it does not ' |
840 | 2eba2338 | Stratos Psomadakis | 'exist.' % volume_name)
|
841 | 2eba2338 | Stratos Psomadakis | |
842 | 2eba2338 | Stratos Psomadakis | vdisk_attributes = self._get_vdisk_attributes(volume_name)
|
843 | 2eba2338 | Stratos Psomadakis | assert(vdisk_attributes['mdisk_grp_name'] == _SVC_POOL_NAME) |
844 | 2eba2338 | Stratos Psomadakis | ssh_cmd = ['svctask', 'rmvdisk', '-force', volume_name] |
845 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
846 | 2eba2338 | Stratos Psomadakis | # No output should be returned from rmvdisk
|
847 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out.strip()) == 0, |
848 | 2eba2338 | Stratos Psomadakis | ('_delete_vdisk %(name)s')
|
849 | 2eba2338 | Stratos Psomadakis | % {'name': volume_name},
|
850 | 2eba2338 | Stratos Psomadakis | ssh_cmd, out, err) |
851 | 2eba2338 | Stratos Psomadakis | |
852 | 2eba2338 | Stratos Psomadakis | def grow(self): |
853 | 2eba2338 | Stratos Psomadakis | """ Resize vdisk. """
|
854 | 2eba2338 | Stratos Psomadakis | volume_name = self._vol_name
|
855 | 2eba2338 | Stratos Psomadakis | |
856 | 2eba2338 | Stratos Psomadakis | if self._vol_size is None: |
857 | 2eba2338 | Stratos Psomadakis | raise SVCError("Missing volume size for grow") |
858 | 2eba2338 | Stratos Psomadakis | |
859 | 2eba2338 | Stratos Psomadakis | new_size = self._vol_size
|
860 | 2eba2338 | Stratos Psomadakis | |
861 | 2eba2338 | Stratos Psomadakis | # size is given in mb, convert to bytes
|
862 | 2eba2338 | Stratos Psomadakis | new_size <<= 20
|
863 | 2eba2338 | Stratos Psomadakis | |
864 | 2eba2338 | Stratos Psomadakis | vdisk_attributes = self._get_vdisk_attributes(volume_name)
|
865 | 2eba2338 | Stratos Psomadakis | old_size = int(vdisk_attributes['capacity']) |
866 | 2eba2338 | Stratos Psomadakis | |
867 | 2eba2338 | Stratos Psomadakis | shrink = old_size > new_size |
868 | 2eba2338 | Stratos Psomadakis | diff = abs(new_size - old_size)
|
869 | 2eba2338 | Stratos Psomadakis | |
870 | 2eba2338 | Stratos Psomadakis | assert(vdisk_attributes['mdisk_grp_name'] == _SVC_POOL_NAME) |
871 | 2eba2338 | Stratos Psomadakis | if shrink:
|
872 | 2eba2338 | Stratos Psomadakis | ssh_cmd = (['svctask', 'shrinkvdisksize', '-size', str(diff), |
873 | 2eba2338 | Stratos Psomadakis | '-unit', 'bytes', volume_name]) |
874 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
875 | 2eba2338 | Stratos Psomadakis | # No output should be returned from expandvdisksize
|
876 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out.strip()) == 0, 'extend_volume', |
877 | 2eba2338 | Stratos Psomadakis | ssh_cmd, out, err) |
878 | 2eba2338 | Stratos Psomadakis | else:
|
879 | 2eba2338 | Stratos Psomadakis | ssh_cmd = (['svctask', 'expandvdisksize', '-size', str(diff), |
880 | 2eba2338 | Stratos Psomadakis | '-unit', 'bytes', volume_name]) |
881 | 2eba2338 | Stratos Psomadakis | out, err = self._svc_connection.run_ssh_cmd(ssh_cmd)
|
882 | 2eba2338 | Stratos Psomadakis | # No output should be returned from expandvdisksize
|
883 | 2eba2338 | Stratos Psomadakis | self._assert_ssh_return(len(out.strip()) == 0, 'extend_volume', |
884 | 2eba2338 | Stratos Psomadakis | ssh_cmd, out, err) |
885 | 2eba2338 | Stratos Psomadakis | |
886 | 2eba2338 | Stratos Psomadakis | def _verify_checklist(self): |
887 | 2eba2338 | Stratos Psomadakis | """ Check for consistent mappings.
|
888 | 2eba2338 | Stratos Psomadakis |
|
889 | 2eba2338 | Stratos Psomadakis | @rtype: list of integers
|
890 | 2eba2338 | Stratos Psomadakis | @return: verification checks
|
891 | 2eba2338 | Stratos Psomadakis |
|
892 | 2eba2338 | Stratos Psomadakis | """
|
893 | 2eba2338 | Stratos Psomadakis | |
894 | 2eba2338 | Stratos Psomadakis | device, lun = self._get_device_info(self._vol_name) |
895 | 2eba2338 | Stratos Psomadakis | |
896 | 2eba2338 | Stratos Psomadakis | check_list = [self._is_vdisk_defined(self._vol_name), |
897 | 2eba2338 | Stratos Psomadakis | self._is_vdisk_mapped(self._vol_name, self._host_id), |
898 | 2eba2338 | Stratos Psomadakis | self._devices_discovered(lun),
|
899 | 2eba2338 | Stratos Psomadakis | self._multipath_ok(device)]
|
900 | 2eba2338 | Stratos Psomadakis | |
901 | 2eba2338 | Stratos Psomadakis | return check_list
|
902 | 2eba2338 | Stratos Psomadakis | |
903 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
904 | 2eba2338 | Stratos Psomadakis | def _consistent_map(check_list): |
905 | 2eba2338 | Stratos Psomadakis | """ Check for consistent map.
|
906 | 2eba2338 | Stratos Psomadakis |
|
907 | 2eba2338 | Stratos Psomadakis | @type value: integer
|
908 | 2eba2338 | Stratos Psomadakis | @param value: verification check sum
|
909 | 2eba2338 | Stratos Psomadakis | @rtype: bool
|
910 | 2eba2338 | Stratos Psomadakis | @return: volume mapped end-to-end or not
|
911 | 2eba2338 | Stratos Psomadakis |
|
912 | 2eba2338 | Stratos Psomadakis | """
|
913 | 2eba2338 | Stratos Psomadakis | return all(check_list) |
914 | 2eba2338 | Stratos Psomadakis | |
915 | 2eba2338 | Stratos Psomadakis | @staticmethod
|
916 | 2eba2338 | Stratos Psomadakis | def _consistent_unmap(check_list): |
917 | 2eba2338 | Stratos Psomadakis | """ Check for consistent unmap.
|
918 | 2eba2338 | Stratos Psomadakis |
|
919 | 2eba2338 | Stratos Psomadakis | @type value: integer
|
920 | 2eba2338 | Stratos Psomadakis | @param value: verification check sum
|
921 | 2eba2338 | Stratos Psomadakis | @rtype: bool
|
922 | 2eba2338 | Stratos Psomadakis | @return: volume mapped end-to-end or not
|
923 | 2eba2338 | Stratos Psomadakis |
|
924 | 2eba2338 | Stratos Psomadakis | """
|
925 | 2eba2338 | Stratos Psomadakis | return not any(check_list) |
926 | 2eba2338 | Stratos Psomadakis | |
927 | 2eba2338 | Stratos Psomadakis | def verify(self): |
928 | 2eba2338 | Stratos Psomadakis | """ Verify op.
|
929 | 2eba2338 | Stratos Psomadakis |
|
930 | 2eba2338 | Stratos Psomadakis | """
|
931 | 2eba2338 | Stratos Psomadakis | if self._vol_size is not None: |
932 | 2eba2338 | Stratos Psomadakis | raise SVCError("Volume size is set, when it shouldn't") |
933 | 2eba2338 | Stratos Psomadakis | |
934 | 2eba2338 | Stratos Psomadakis | if not self._consistent_map(self._verify_checklist()): |
935 | 2eba2338 | Stratos Psomadakis | raise SVCError("Verify op failed") |
936 | 2eba2338 | Stratos Psomadakis | |
937 | 2eba2338 | Stratos Psomadakis | |
938 | 2eba2338 | Stratos Psomadakis | def main(): |
939 | 2eba2338 | Stratos Psomadakis | """Read env variables and SAN conf and branch to the requested function.
|
940 | 2eba2338 | Stratos Psomadakis |
|
941 | 2eba2338 | Stratos Psomadakis | """
|
942 | 2eba2338 | Stratos Psomadakis | |
943 | 2eba2338 | Stratos Psomadakis | try:
|
944 | 2eba2338 | Stratos Psomadakis | svc = SVCProvider(_SVC_CONFIG_PATH) |
945 | 2eba2338 | Stratos Psomadakis | |
946 | 2eba2338 | Stratos Psomadakis | try:
|
947 | 2eba2338 | Stratos Psomadakis | action = { |
948 | 2eba2338 | Stratos Psomadakis | 'attach': svc.attach,
|
949 | 2eba2338 | Stratos Psomadakis | 'create': svc.create,
|
950 | 2eba2338 | Stratos Psomadakis | 'detach': svc.detach,
|
951 | 2eba2338 | Stratos Psomadakis | 'grow': svc.grow,
|
952 | 2eba2338 | Stratos Psomadakis | 'remove': svc.remove,
|
953 | 2eba2338 | Stratos Psomadakis | 'verify': svc.verify,
|
954 | 2eba2338 | Stratos Psomadakis | }[os.path.basename(sys.argv[0])]
|
955 | 2eba2338 | Stratos Psomadakis | except KeyError: |
956 | 2eba2338 | Stratos Psomadakis | sys.stderr.write("Op not supported\n")
|
957 | 2eba2338 | Stratos Psomadakis | return 1 |
958 | 2eba2338 | Stratos Psomadakis | |
959 | 2eba2338 | Stratos Psomadakis | action() |
960 | 2eba2338 | Stratos Psomadakis | return 0 |
961 | 2eba2338 | Stratos Psomadakis | except SVCError as exc: |
962 | 2eba2338 | Stratos Psomadakis | sys.stderr.write("%s\n" % exc)
|
963 | 2eba2338 | Stratos Psomadakis | except Exception as exc: |
964 | 2eba2338 | Stratos Psomadakis | sys.stderr.write(traceback.format_exc()) |
965 | 2eba2338 | Stratos Psomadakis | sys.stderr.write("Unexpected error: %s\n" % exc)
|
966 | 2eba2338 | Stratos Psomadakis | |
967 | 2eba2338 | Stratos Psomadakis | return 1 |
968 | 2eba2338 | Stratos Psomadakis | |
969 | 2eba2338 | Stratos Psomadakis | if __name__ == '__main__': |
970 | 2eba2338 | Stratos Psomadakis | sys.exit(main()) |