blob: 929a584d62cbb7f5ba8c838248f468d4c31a2609 [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)
89 self._workspace_name_exists(new_name)
90 self.workspaces[new_name] = self.workspaces.pop(old_name)
91 self._write_file()
92
93 @lockutils.synchronized('workspaces', external=True)
94 def move_workspace(self, name, path):
95 self._populate()
96 path = os.path.abspath(os.path.expanduser(path))
97 self._name_exists(name)
98 self._validate_path(path)
99 self.workspaces[name] = path
100 self._write_file()
101
102 def _name_exists(self, name):
103 if name not in self.workspaces:
104 print("A workspace was not found with name: {0}".format(name))
105 sys.exit(1)
106
107 @lockutils.synchronized('workspaces', external=True)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530108 def remove_workspace_entry(self, name):
step682980c14ec2016-02-23 14:53:52 -0500109 self._populate()
110 self._name_exists(name)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530111 workspace_path = self.workspaces.pop(name)
step682980c14ec2016-02-23 14:53:52 -0500112 self._write_file()
Chandan Kumarb78e2572017-06-26 19:34:34 +0530113 return workspace_path
114
115 @lockutils.synchronized('workspaces', external=True)
116 def remove_workspace_directory(self, workspace_path):
117 shutil.rmtree(workspace_path)
step682980c14ec2016-02-23 14:53:52 -0500118
119 @lockutils.synchronized('workspaces', external=True)
120 def list_workspaces(self):
121 self._populate()
122 self._validate_workspaces()
123 return self.workspaces
124
125 def _workspace_name_exists(self, name):
126 if name in self.workspaces:
127 print("A workspace already exists with name: {0}.".format(
128 name))
129 sys.exit(1)
130
131 def _validate_path(self, path):
132 if not os.path.exists(path):
133 print("Path does not exist.")
134 sys.exit(1)
135
136 @lockutils.synchronized('workspaces', external=True)
137 def register_new_workspace(self, name, path, init=False):
138 """Adds the new workspace and writes out the new workspace config"""
139 self._populate()
140 path = os.path.abspath(os.path.expanduser(path))
141 # This only happens when register is called from outside of init
142 if not init:
143 self._validate_path(path)
144 self._workspace_name_exists(name)
145 self.workspaces[name] = path
146 self._write_file()
147
148 def _validate_workspaces(self):
149 if self.workspaces is not None:
150 self.workspaces = {n: p for n, p in self.workspaces.items()
151 if os.path.exists(p)}
152 self._write_file()
153
154 def _write_file(self):
155 with open(self.path, 'w') as f:
156 f.write(yaml.dump(self.workspaces))
157
158 def _populate(self):
159 if not os.path.isfile(self.path):
160 return
161 with open(self.path, 'r') as f:
Dao Cong Tien40d02082017-01-16 16:59:18 +0700162 self.workspaces = yaml.safe_load(f) or {}
step682980c14ec2016-02-23 14:53:52 -0500163
164
Masayuki Igawa00effc92016-07-25 12:28:26 +0900165def add_global_arguments(parser):
166 parser.add_argument(
167 '--workspace-path', required=False, default=None,
168 help="The path to the workspace file, the default is "
169 "~/.tempest/workspace.yaml")
170 return parser
step682980c14ec2016-02-23 14:53:52 -0500171
Masayuki Igawa00effc92016-07-25 12:28:26 +0900172
173class TempestWorkspaceRegister(command.Command):
step682980c14ec2016-02-23 14:53:52 -0500174 def get_description(self):
Masayuki Igawa00effc92016-07-25 12:28:26 +0900175 return ('Registers a new tempest workspace via a given '
176 '--name and --path')
step682980c14ec2016-02-23 14:53:52 -0500177
178 def get_parser(self, prog_name):
Masayuki Igawa00effc92016-07-25 12:28:26 +0900179 parser = super(TempestWorkspaceRegister, self).get_parser(prog_name)
180 add_global_arguments(parser)
181 parser.add_argument('--name', required=True)
182 parser.add_argument('--path', required=True)
step682980c14ec2016-02-23 14:53:52 -0500183
184 return parser
185
Masayuki Igawa00effc92016-07-25 12:28:26 +0900186 def take_action(self, parsed_args):
187 self.manager = WorkspaceManager(parsed_args.workspace_path)
188 self.manager.register_new_workspace(parsed_args.name, parsed_args.path)
189 sys.exit(0)
step682980c14ec2016-02-23 14:53:52 -0500190
Masayuki Igawa00effc92016-07-25 12:28:26 +0900191
192class TempestWorkspaceRename(command.Command):
193 def get_description(self):
194 return 'Renames a tempest workspace from --old-name to --new-name'
195
196 def get_parser(self, prog_name):
197 parser = super(TempestWorkspaceRename, self).get_parser(prog_name)
198 add_global_arguments(parser)
199 parser.add_argument('--old-name', required=True)
200 parser.add_argument('--new-name', required=True)
201
202 return parser
203
204 def take_action(self, parsed_args):
205 self.manager = WorkspaceManager(parsed_args.workspace_path)
206 self.manager.rename_workspace(
207 parsed_args.old_name, parsed_args.new_name)
208 sys.exit(0)
209
210
211class TempestWorkspaceMove(command.Command):
212 def get_description(self):
213 return 'Changes the path of a given tempest workspace --name to --path'
214
215 def get_parser(self, prog_name):
216 parser = super(TempestWorkspaceMove, self).get_parser(prog_name)
217 add_global_arguments(parser)
218 parser.add_argument('--name', required=True)
219 parser.add_argument('--path', required=True)
220
221 return parser
222
223 def take_action(self, parsed_args):
224 self.manager = WorkspaceManager(parsed_args.workspace_path)
225 self.manager.move_workspace(parsed_args.name, parsed_args.path)
226 sys.exit(0)
227
228
229class TempestWorkspaceRemove(command.Command):
230 def get_description(self):
231 return 'Deletes the entry for a given tempest workspace --name'
232
233 def get_parser(self, prog_name):
234 parser = super(TempestWorkspaceRemove, self).get_parser(prog_name)
235 add_global_arguments(parser)
236 parser.add_argument('--name', required=True)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530237 parser.add_argument('--rmdir', action='store_true',
238 help='Deletes the given workspace directory')
Masayuki Igawa00effc92016-07-25 12:28:26 +0900239
240 return parser
241
242 def take_action(self, parsed_args):
243 self.manager = WorkspaceManager(parsed_args.workspace_path)
Chandan Kumarb78e2572017-06-26 19:34:34 +0530244 workspace_path = self.manager.remove_workspace_entry(parsed_args.name)
245 if parsed_args.rmdir:
246 self.manager.remove_workspace_directory(workspace_path)
Masayuki Igawa00effc92016-07-25 12:28:26 +0900247 sys.exit(0)
248
249
250class TempestWorkspaceList(lister.Lister):
251 def get_description(self):
252 return 'Outputs the name and path of all known tempest workspaces'
253
254 def get_parser(self, prog_name):
255 parser = super(TempestWorkspaceList, self).get_parser(prog_name)
256 add_global_arguments(parser)
257 return parser
258
259 def take_action(self, parsed_args):
260 self.manager = WorkspaceManager(parsed_args.workspace_path)
261 return (("Name", "Path"),
262 ((n, p) for n, p in self.manager.list_workspaces().items()))