blob: 3c58648b961024c2a5c9e39c090761934e403878 [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--------
29Registers a new tempest workspace via a given --name and --path
30
31rename
32------
33Renames a tempest workspace from --old-name to --new-name
34
35move
36----
37Changes the path of a given tempest workspace --name to --path
38
39remove
40------
41Deletes the entry for a given tempest workspace --name
42
43General Options
44===============
45
46 **--workspace_path**: Allows the user to specify a different location for the
47 workspace.yaml file containing the workspace definitions
48 instead of ~/.tempest/workspace.yaml
49"""
50
51import os
52import sys
53
54from cliff import command
55from oslo_concurrency import lockutils
step682980c14ec2016-02-23 14:53:52 -050056import prettytable
57import yaml
58
59from tempest import config
60
step682980c14ec2016-02-23 14:53:52 -050061CONF = config.CONF
62
63
64class WorkspaceManager(object):
65 def __init__(self, path=None):
66 lockutils.get_lock_path(CONF)
67 self.path = path or os.path.join(
68 os.path.expanduser("~"), ".tempest", "workspace.yaml")
69 if not os.path.isdir(os.path.dirname(self.path)):
70 os.makedirs(self.path.rsplit(os.path.sep, 1)[0])
71 self.workspaces = {}
72
73 @lockutils.synchronized('workspaces', external=True)
74 def get_workspace(self, name):
Brant Knudson6a090f42016-10-13 12:51:49 -050075 """Returns the workspace that has the given name
76
77 If the workspace isn't registered then `None` is returned.
78 """
step682980c14ec2016-02-23 14:53:52 -050079 self._populate()
80 return self.workspaces.get(name)
81
82 @lockutils.synchronized('workspaces', external=True)
83 def rename_workspace(self, old_name, new_name):
84 self._populate()
85 self._name_exists(old_name)
86 self._workspace_name_exists(new_name)
87 self.workspaces[new_name] = self.workspaces.pop(old_name)
88 self._write_file()
89
90 @lockutils.synchronized('workspaces', external=True)
91 def move_workspace(self, name, path):
92 self._populate()
93 path = os.path.abspath(os.path.expanduser(path))
94 self._name_exists(name)
95 self._validate_path(path)
96 self.workspaces[name] = path
97 self._write_file()
98
99 def _name_exists(self, name):
100 if name not in self.workspaces:
101 print("A workspace was not found with name: {0}".format(name))
102 sys.exit(1)
103
104 @lockutils.synchronized('workspaces', external=True)
105 def remove_workspace(self, name):
106 self._populate()
107 self._name_exists(name)
108 self.workspaces.pop(name)
109 self._write_file()
110
111 @lockutils.synchronized('workspaces', external=True)
112 def list_workspaces(self):
113 self._populate()
114 self._validate_workspaces()
115 return self.workspaces
116
117 def _workspace_name_exists(self, name):
118 if name in self.workspaces:
119 print("A workspace already exists with name: {0}.".format(
120 name))
121 sys.exit(1)
122
123 def _validate_path(self, path):
124 if not os.path.exists(path):
125 print("Path does not exist.")
126 sys.exit(1)
127
128 @lockutils.synchronized('workspaces', external=True)
129 def register_new_workspace(self, name, path, init=False):
130 """Adds the new workspace and writes out the new workspace config"""
131 self._populate()
132 path = os.path.abspath(os.path.expanduser(path))
133 # This only happens when register is called from outside of init
134 if not init:
135 self._validate_path(path)
136 self._workspace_name_exists(name)
137 self.workspaces[name] = path
138 self._write_file()
139
140 def _validate_workspaces(self):
141 if self.workspaces is not None:
142 self.workspaces = {n: p for n, p in self.workspaces.items()
143 if os.path.exists(p)}
144 self._write_file()
145
146 def _write_file(self):
147 with open(self.path, 'w') as f:
148 f.write(yaml.dump(self.workspaces))
149
150 def _populate(self):
151 if not os.path.isfile(self.path):
152 return
153 with open(self.path, 'r') as f:
154 self.workspaces = yaml.load(f) or {}
155
156
157class TempestWorkspace(command.Command):
158 def take_action(self, parsed_args):
159 self.manager = WorkspaceManager(parsed_args.workspace_path)
160 if getattr(parsed_args, 'register', None):
161 self.manager.register_new_workspace(
162 parsed_args.name, parsed_args.path)
163 elif getattr(parsed_args, 'rename', None):
164 self.manager.rename_workspace(
165 parsed_args.old_name, parsed_args.new_name)
166 elif getattr(parsed_args, 'move', None):
167 self.manager.move_workspace(
168 parsed_args.name, parsed_args.path)
169 elif getattr(parsed_args, 'remove', None):
170 self.manager.remove_workspace(
171 parsed_args.name)
172 else:
173 self._print_workspaces()
174 sys.exit(0)
175
176 def get_description(self):
177 return 'Tempest workspace actions'
178
179 def get_parser(self, prog_name):
180 parser = super(TempestWorkspace, self).get_parser(prog_name)
181
182 parser.add_argument(
183 '--workspace-path', required=False, default=None,
184 help="The path to the workspace file, the default is "
185 "~/.tempest/workspace.yaml")
186
187 subparsers = parser.add_subparsers()
188
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900189 list_parser = subparsers.add_parser(
190 'list', help='Outputs the name and path of all known tempest '
191 'workspaces')
step682980c14ec2016-02-23 14:53:52 -0500192 list_parser.set_defaults(list=True)
193
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900194 register_parser = subparsers.add_parser(
195 'register', help='Registers a new tempest workspace via a given '
196 '--name and --path')
step682980c14ec2016-02-23 14:53:52 -0500197 register_parser.add_argument('--name', required=True)
198 register_parser.add_argument('--path', required=True)
199 register_parser.set_defaults(register=True)
200
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900201 update_parser = subparsers.add_parser(
202 'rename', help='Renames a tempest workspace from --old-name to '
203 '--new-name')
step682980c14ec2016-02-23 14:53:52 -0500204 update_parser.add_argument('--old-name', required=True)
205 update_parser.add_argument('--new-name', required=True)
206 update_parser.set_defaults(rename=True)
207
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900208 move_parser = subparsers.add_parser(
209 'move', help='Changes the path of a given tempest workspace '
210 '--name to --path')
step682980c14ec2016-02-23 14:53:52 -0500211 move_parser.add_argument('--name', required=True)
212 move_parser.add_argument('--path', required=True)
213 move_parser.set_defaults(move=True)
214
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900215 remove_parser = subparsers.add_parser(
216 'remove', help='Deletes the entry for a given tempest workspace '
217 '--name')
step682980c14ec2016-02-23 14:53:52 -0500218 remove_parser.add_argument('--name', required=True)
219 remove_parser.set_defaults(remove=True)
220
221 return parser
222
223 def _print_workspaces(self):
224 output = prettytable.PrettyTable(["Name", "Path"])
225 if self.manager.list_workspaces() is not None:
226 for name, path in self.manager.list_workspaces().items():
227 output.add_row([name, path])
228
229 print(output)