blob: d0c4b281a082f1bcd55f990b545d13098dad8271 [file] [log] [blame]
step682980c14ec2016-02-23 14:53:52 -05001# Copyright 2016 Rackspace
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15"""
16Manages Tempest workspaces
17
18This command is used for managing tempest workspaces
19
20Commands
21========
22
23list
24----
25Outputs the name and path of all known tempest workspaces
26
27register
28--------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090029Registers a new tempest workspace via a given ``--name`` and ``--path``
step682980c14ec2016-02-23 14:53:52 -050030
31rename
32------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090033Renames a tempest workspace from ``--old-name`` to ``--new-name``
step682980c14ec2016-02-23 14:53:52 -050034
35move
36----
Masayuki Igawabbbaad62017-11-21 16:04:03 +090037Changes the path of a given tempest workspace ``--name`` to ``--path``
step682980c14ec2016-02-23 14:53:52 -050038
39remove
40------
Masayuki Igawabbbaad62017-11-21 16:04:03 +090041Deletes the entry for a given tempest workspace ``--name``
step682980c14ec2016-02-23 14:53:52 -050042
Masayuki Igawabbbaad62017-11-21 16:04:03 +090043``--rmdir`` Deletes the given tempest workspace directory
Chandan Kumarb78e2572017-06-26 19:34:34 +053044
step682980c14ec2016-02-23 14:53:52 -050045General Options
46===============
47
Masayuki Igawabbbaad62017-11-21 16:04:03 +090048* ``--workspace_path``: Allows the user to specify a different location for the
49 workspace.yaml file containing the workspace definitions instead of
50 ``~/.tempest/workspace.yaml``
step682980c14ec2016-02-23 14:53:52 -050051"""
52
53import os
Chandan Kumarb78e2572017-06-26 19:34:34 +053054import shutil
step682980c14ec2016-02-23 14:53:52 -050055import sys
56
57from cliff import command
Masayuki Igawa00effc92016-07-25 12:28:26 +090058from cliff import lister
step682980c14ec2016-02-23 14:53:52 -050059from oslo_concurrency import lockutils
step682980c14ec2016-02-23 14:53:52 -050060import yaml
61
62from tempest import config
63
step682980c14ec2016-02-23 14:53:52 -050064CONF = config.CONF
65
66
67class WorkspaceManager(object):
68 def __init__(self, path=None):
69 lockutils.get_lock_path(CONF)
70 self.path = path or os.path.join(
71 os.path.expanduser("~"), ".tempest", "workspace.yaml")
72 if not os.path.isdir(os.path.dirname(self.path)):
73 os.makedirs(self.path.rsplit(os.path.sep, 1)[0])
74 self.workspaces = {}
75
76 @lockutils.synchronized('workspaces', external=True)
77 def get_workspace(self, name):
Brant Knudson6a090f42016-10-13 12:51:49 -050078 """Returns the workspace that has the given name
79
80 If the workspace isn't registered then `None` is returned.
81 """
step682980c14ec2016-02-23 14:53:52 -050082 self._populate()
83 return self.workspaces.get(name)
84
85 @lockutils.synchronized('workspaces', external=True)
86 def rename_workspace(self, old_name, new_name):
87 self._populate()
88 self._name_exists(old_name)
Manik Bindlish864f37e2018-09-06 06:30:51 +000089 self._invalid_name_check(new_name)
step682980c14ec2016-02-23 14:53:52 -050090 self._workspace_name_exists(new_name)
91 self.workspaces[new_name] = self.workspaces.pop(old_name)
92 self._write_file()
93
94 @lockutils.synchronized('workspaces', external=True)
95 def move_workspace(self, name, path):
96 self._populate()
Manik Bindlish6c956782018-10-25 06:59:55 +000097 path = os.path.abspath(os.path.expanduser(path)) if path else path
step682980c14ec2016-02-23 14:53:52 -050098 self._name_exists(name)
99 self._validate_path(path)
100 self.workspaces[name] = path
101 self._write_file()
102
103 def _name_exists(self, name):
104 if name not in self.workspaces:
105 print("A workspace was not found with name: {0}".format(name))
106 sys.exit(1)
107
108 @lockutils.synchronized('workspaces', external=True)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530109 def remove_workspace_entry(self, name):
step682980c14ec2016-02-23 14:53:52 -0500110 self._populate()
111 self._name_exists(name)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530112 workspace_path = self.workspaces.pop(name)
step682980c14ec2016-02-23 14:53:52 -0500113 self._write_file()
Chandan Kumarb78e2572017-06-26 19:34:34 +0530114 return workspace_path
115
116 @lockutils.synchronized('workspaces', external=True)
117 def remove_workspace_directory(self, workspace_path):
Manik Bindlish6c956782018-10-25 06:59:55 +0000118 self._validate_path(workspace_path)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530119 shutil.rmtree(workspace_path)
step682980c14ec2016-02-23 14:53:52 -0500120
121 @lockutils.synchronized('workspaces', external=True)
122 def list_workspaces(self):
123 self._populate()
124 self._validate_workspaces()
125 return self.workspaces
126
127 def _workspace_name_exists(self, name):
128 if name in self.workspaces:
129 print("A workspace already exists with name: {0}.".format(
130 name))
131 sys.exit(1)
132
Manik Bindlish864f37e2018-09-06 06:30:51 +0000133 def _invalid_name_check(self, name):
134 if not name:
135 print("None or empty name is specified."
136 " Please specify correct name for workspace.")
137 sys.exit(1)
138
step682980c14ec2016-02-23 14:53:52 -0500139 def _validate_path(self, path):
Manik Bindlish6c956782018-10-25 06:59:55 +0000140 if not path:
141 print("None or empty path is specified for workspace."
142 " Please specify correct workspace path.")
143 sys.exit(1)
step682980c14ec2016-02-23 14:53:52 -0500144 if not os.path.exists(path):
145 print("Path does not exist.")
146 sys.exit(1)
147
148 @lockutils.synchronized('workspaces', external=True)
149 def register_new_workspace(self, name, path, init=False):
150 """Adds the new workspace and writes out the new workspace config"""
151 self._populate()
Manik Bindlish6c956782018-10-25 06:59:55 +0000152 path = os.path.abspath(os.path.expanduser(path)) if path else path
step682980c14ec2016-02-23 14:53:52 -0500153 # This only happens when register is called from outside of init
154 if not init:
155 self._validate_path(path)
Manik Bindlish864f37e2018-09-06 06:30:51 +0000156 self._invalid_name_check(name)
step682980c14ec2016-02-23 14:53:52 -0500157 self._workspace_name_exists(name)
158 self.workspaces[name] = path
159 self._write_file()
160
161 def _validate_workspaces(self):
162 if self.workspaces is not None:
163 self.workspaces = {n: p for n, p in self.workspaces.items()
164 if os.path.exists(p)}
165 self._write_file()
166
167 def _write_file(self):
168 with open(self.path, 'w') as f:
169 f.write(yaml.dump(self.workspaces))
170
171 def _populate(self):
172 if not os.path.isfile(self.path):
173 return
174 with open(self.path, 'r') as f:
Dao Cong Tien40d02082017-01-16 16:59:18 +0700175 self.workspaces = yaml.safe_load(f) or {}
step682980c14ec2016-02-23 14:53:52 -0500176
177
Masayuki Igawa00effc92016-07-25 12:28:26 +0900178def add_global_arguments(parser):
179 parser.add_argument(
180 '--workspace-path', required=False, default=None,
181 help="The path to the workspace file, the default is "
182 "~/.tempest/workspace.yaml")
183 return parser
step682980c14ec2016-02-23 14:53:52 -0500184
Masayuki Igawa00effc92016-07-25 12:28:26 +0900185
186class TempestWorkspaceRegister(command.Command):
step682980c14ec2016-02-23 14:53:52 -0500187 def get_description(self):
Masayuki Igawa00effc92016-07-25 12:28:26 +0900188 return ('Registers a new tempest workspace via a given '
189 '--name and --path')
step682980c14ec2016-02-23 14:53:52 -0500190
191 def get_parser(self, prog_name):
Masayuki Igawa00effc92016-07-25 12:28:26 +0900192 parser = super(TempestWorkspaceRegister, self).get_parser(prog_name)
193 add_global_arguments(parser)
194 parser.add_argument('--name', required=True)
195 parser.add_argument('--path', required=True)
step682980c14ec2016-02-23 14:53:52 -0500196
197 return parser
198
Masayuki Igawa00effc92016-07-25 12:28:26 +0900199 def take_action(self, parsed_args):
200 self.manager = WorkspaceManager(parsed_args.workspace_path)
201 self.manager.register_new_workspace(parsed_args.name, parsed_args.path)
202 sys.exit(0)
step682980c14ec2016-02-23 14:53:52 -0500203
Masayuki Igawa00effc92016-07-25 12:28:26 +0900204
205class TempestWorkspaceRename(command.Command):
206 def get_description(self):
207 return 'Renames a tempest workspace from --old-name to --new-name'
208
209 def get_parser(self, prog_name):
210 parser = super(TempestWorkspaceRename, self).get_parser(prog_name)
211 add_global_arguments(parser)
212 parser.add_argument('--old-name', required=True)
213 parser.add_argument('--new-name', required=True)
214
215 return parser
216
217 def take_action(self, parsed_args):
218 self.manager = WorkspaceManager(parsed_args.workspace_path)
219 self.manager.rename_workspace(
220 parsed_args.old_name, parsed_args.new_name)
221 sys.exit(0)
222
223
224class TempestWorkspaceMove(command.Command):
225 def get_description(self):
226 return 'Changes the path of a given tempest workspace --name to --path'
227
228 def get_parser(self, prog_name):
229 parser = super(TempestWorkspaceMove, self).get_parser(prog_name)
230 add_global_arguments(parser)
231 parser.add_argument('--name', required=True)
232 parser.add_argument('--path', required=True)
233
234 return parser
235
236 def take_action(self, parsed_args):
237 self.manager = WorkspaceManager(parsed_args.workspace_path)
238 self.manager.move_workspace(parsed_args.name, parsed_args.path)
239 sys.exit(0)
240
241
242class TempestWorkspaceRemove(command.Command):
243 def get_description(self):
244 return 'Deletes the entry for a given tempest workspace --name'
245
246 def get_parser(self, prog_name):
247 parser = super(TempestWorkspaceRemove, self).get_parser(prog_name)
248 add_global_arguments(parser)
249 parser.add_argument('--name', required=True)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530250 parser.add_argument('--rmdir', action='store_true',
251 help='Deletes the given workspace directory')
Masayuki Igawa00effc92016-07-25 12:28:26 +0900252
253 return parser
254
255 def take_action(self, parsed_args):
256 self.manager = WorkspaceManager(parsed_args.workspace_path)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530257 workspace_path = self.manager.remove_workspace_entry(parsed_args.name)
258 if parsed_args.rmdir:
259 self.manager.remove_workspace_directory(workspace_path)
Masayuki Igawa00effc92016-07-25 12:28:26 +0900260 sys.exit(0)
261
262
263class TempestWorkspaceList(lister.Lister):
264 def get_description(self):
265 return 'Outputs the name and path of all known tempest workspaces'
266
267 def get_parser(self, prog_name):
268 parser = super(TempestWorkspaceList, self).get_parser(prog_name)
269 add_global_arguments(parser)
270 return parser
271
272 def take_action(self, parsed_args):
273 self.manager = WorkspaceManager(parsed_args.workspace_path)
274 return (("Name", "Path"),
275 ((n, p) for n, p in self.manager.list_workspaces().items()))