blob: cc82284f3f227de6e6f403a51611a6e7c56ff8e7 [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
56from oslo_log import log as logging
57import prettytable
58import yaml
59
60from tempest import config
61
62LOG = logging.getLogger(__name__)
63CONF = config.CONF
64
65
66class WorkspaceManager(object):
67 def __init__(self, path=None):
68 lockutils.get_lock_path(CONF)
69 self.path = path or os.path.join(
70 os.path.expanduser("~"), ".tempest", "workspace.yaml")
71 if not os.path.isdir(os.path.dirname(self.path)):
72 os.makedirs(self.path.rsplit(os.path.sep, 1)[0])
73 self.workspaces = {}
74
75 @lockutils.synchronized('workspaces', external=True)
76 def get_workspace(self, name):
77 """Returns the workspace that has the given name"""
78 self._populate()
79 return self.workspaces.get(name)
80
81 @lockutils.synchronized('workspaces', external=True)
82 def rename_workspace(self, old_name, new_name):
83 self._populate()
84 self._name_exists(old_name)
85 self._workspace_name_exists(new_name)
86 self.workspaces[new_name] = self.workspaces.pop(old_name)
87 self._write_file()
88
89 @lockutils.synchronized('workspaces', external=True)
90 def move_workspace(self, name, path):
91 self._populate()
92 path = os.path.abspath(os.path.expanduser(path))
93 self._name_exists(name)
94 self._validate_path(path)
95 self.workspaces[name] = path
96 self._write_file()
97
98 def _name_exists(self, name):
99 if name not in self.workspaces:
100 print("A workspace was not found with name: {0}".format(name))
101 sys.exit(1)
102
103 @lockutils.synchronized('workspaces', external=True)
104 def remove_workspace(self, name):
105 self._populate()
106 self._name_exists(name)
107 self.workspaces.pop(name)
108 self._write_file()
109
110 @lockutils.synchronized('workspaces', external=True)
111 def list_workspaces(self):
112 self._populate()
113 self._validate_workspaces()
114 return self.workspaces
115
116 def _workspace_name_exists(self, name):
117 if name in self.workspaces:
118 print("A workspace already exists with name: {0}.".format(
119 name))
120 sys.exit(1)
121
122 def _validate_path(self, path):
123 if not os.path.exists(path):
124 print("Path does not exist.")
125 sys.exit(1)
126
127 @lockutils.synchronized('workspaces', external=True)
128 def register_new_workspace(self, name, path, init=False):
129 """Adds the new workspace and writes out the new workspace config"""
130 self._populate()
131 path = os.path.abspath(os.path.expanduser(path))
132 # This only happens when register is called from outside of init
133 if not init:
134 self._validate_path(path)
135 self._workspace_name_exists(name)
136 self.workspaces[name] = path
137 self._write_file()
138
139 def _validate_workspaces(self):
140 if self.workspaces is not None:
141 self.workspaces = {n: p for n, p in self.workspaces.items()
142 if os.path.exists(p)}
143 self._write_file()
144
145 def _write_file(self):
146 with open(self.path, 'w') as f:
147 f.write(yaml.dump(self.workspaces))
148
149 def _populate(self):
150 if not os.path.isfile(self.path):
151 return
152 with open(self.path, 'r') as f:
153 self.workspaces = yaml.load(f) or {}
154
155
156class TempestWorkspace(command.Command):
157 def take_action(self, parsed_args):
158 self.manager = WorkspaceManager(parsed_args.workspace_path)
159 if getattr(parsed_args, 'register', None):
160 self.manager.register_new_workspace(
161 parsed_args.name, parsed_args.path)
162 elif getattr(parsed_args, 'rename', None):
163 self.manager.rename_workspace(
164 parsed_args.old_name, parsed_args.new_name)
165 elif getattr(parsed_args, 'move', None):
166 self.manager.move_workspace(
167 parsed_args.name, parsed_args.path)
168 elif getattr(parsed_args, 'remove', None):
169 self.manager.remove_workspace(
170 parsed_args.name)
171 else:
172 self._print_workspaces()
173 sys.exit(0)
174
175 def get_description(self):
176 return 'Tempest workspace actions'
177
178 def get_parser(self, prog_name):
179 parser = super(TempestWorkspace, self).get_parser(prog_name)
180
181 parser.add_argument(
182 '--workspace-path', required=False, default=None,
183 help="The path to the workspace file, the default is "
184 "~/.tempest/workspace.yaml")
185
186 subparsers = parser.add_subparsers()
187
188 list_parser = subparsers.add_parser('list')
189 list_parser.set_defaults(list=True)
190
191 register_parser = subparsers.add_parser('register')
192 register_parser.add_argument('--name', required=True)
193 register_parser.add_argument('--path', required=True)
194 register_parser.set_defaults(register=True)
195
196 update_parser = subparsers.add_parser('rename')
197 update_parser.add_argument('--old-name', required=True)
198 update_parser.add_argument('--new-name', required=True)
199 update_parser.set_defaults(rename=True)
200
201 move_parser = subparsers.add_parser('move')
202 move_parser.add_argument('--name', required=True)
203 move_parser.add_argument('--path', required=True)
204 move_parser.set_defaults(move=True)
205
206 remove_parser = subparsers.add_parser('remove')
207 remove_parser.add_argument('--name', required=True)
208 remove_parser.set_defaults(remove=True)
209
210 return parser
211
212 def _print_workspaces(self):
213 output = prettytable.PrettyTable(["Name", "Path"])
214 if self.manager.list_workspaces() is not None:
215 for name, path in self.manager.list_workspaces().items():
216 output.add_row([name, path])
217
218 print(output)