# See the License for the specific language governing permissions and
# limitations under the License.
+import os
import sys
# We always use rtslib-fb, but until version 2.1.52 it didn't have its own
def save_to_file(destination_file):
rtsroot = rtslib_fb.root.RTSRoot()
try:
- rtsroot.save_to_file(destination_file)
- except OSError:
+ # If default destination use rtslib default save file
if not destination_file:
- destination_file = 'default location'
- raise RtstoolError(_('Could not save configuration to %(file_path)s'),
- {'file_path': destination_file})
+ destination_file = rtslib_fb.root.default_save_file
+ path_to_file = os.path.dirname(destination_file)
+
+ # NOTE(geguileo): With default file we ensure path exists and
+ # create it if doesn't.
+ # Cinder's LIO target helper runs this as root, so it will have no
+ # problem creating directory /etc/target.
+ # If run manually from the command line without being root you will
+ # get an error, same as when creating and removing targets.
+ if not os.path.exists(path_to_file):
+ os.makedirs(path_to_file, 0o755)
+
+ except OSError as exc:
+ raise RtstoolError(_('targetcli not installed and could not create '
+ 'default directory (%(default_path)s): %(exc)s'),
+ {'default_path': path_to_file, 'exc': exc})
+ try:
+ rtsroot.save_to_file(destination_file)
+ except (OSError, IOError) as exc:
+ raise RtstoolError(_('Could not save configuration to %(file_path)s: '
+ '%(exc)s'),
+ {'file_path': destination_file, 'exc': exc})
def parse_optional_create(argv):
target.delete.assert_called_once_with()
storage_object.delete.assert_called_once_with()
+ @mock.patch.object(cinder_rtstool, 'os', autospec=True)
@mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True)
- def test_save(self, mock_rtslib):
+ def test_save_with_filename(self, mock_rtslib, mock_os):
filename = mock.sentinel.filename
cinder_rtstool.save_to_file(filename)
rtsroot = mock_rtslib.root.RTSRoot
rtsroot.assert_called_once_with()
+ self.assertEqual(0, mock_os.path.dirname.call_count)
+ self.assertEqual(0, mock_os.path.exists.call_count)
+ self.assertEqual(0, mock_os.makedirs.call_count)
rtsroot.return_value.save_to_file.assert_called_once_with(filename)
+ @mock.patch.object(cinder_rtstool, 'os',
+ **{'path.exists.return_value': True,
+ 'path.dirname.return_value': mock.sentinel.dirname})
+ @mock.patch.object(cinder_rtstool, 'rtslib_fb',
+ **{'root.default_save_file': mock.sentinel.filename})
+ def test_save(self, mock_rtslib, mock_os):
+ """Test that we check path exists with default file."""
+ cinder_rtstool.save_to_file(None)
+ rtsroot = mock_rtslib.root.RTSRoot
+ rtsroot.assert_called_once_with()
+ rtsroot.return_value.save_to_file.assert_called_once_with(
+ mock.sentinel.filename)
+ mock_os.path.dirname.assert_called_once_with(mock.sentinel.filename)
+ mock_os.path.exists.assert_called_once_with(mock.sentinel.dirname)
+ self.assertEqual(0, mock_os.makedirs.call_count)
+
+ @mock.patch.object(cinder_rtstool, 'os',
+ **{'path.exists.return_value': False,
+ 'path.dirname.return_value': mock.sentinel.dirname})
+ @mock.patch.object(cinder_rtstool, 'rtslib_fb',
+ **{'root.default_save_file': mock.sentinel.filename})
+ def test_save_no_targetcli(self, mock_rtslib, mock_os):
+ """Test that we create path if it doesn't exist with default file."""
+ cinder_rtstool.save_to_file(None)
+ rtsroot = mock_rtslib.root.RTSRoot
+ rtsroot.assert_called_once_with()
+ rtsroot.return_value.save_to_file.assert_called_once_with(
+ mock.sentinel.filename)
+ mock_os.path.dirname.assert_called_once_with(mock.sentinel.filename)
+ mock_os.path.exists.assert_called_once_with(mock.sentinel.dirname)
+ mock_os.makedirs.assert_called_once_with(mock.sentinel.dirname, 0o755)
+
def test_usage(self):
with mock.patch('sys.stdout', new=six.StringIO()):
exit = self.assertRaises(SystemExit, cinder_rtstool.usage)