blob: b36cf4ebed35644ce603558de70a3179d7d1e70d [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):
75 """Returns the workspace that has the given name"""
76 self._populate()
77 return self.workspaces.get(name)
78
79 @lockutils.synchronized('workspaces', external=True)
80 def rename_workspace(self, old_name, new_name):
81 self._populate()
82 self._name_exists(old_name)
83 self._workspace_name_exists(new_name)
84 self.workspaces[new_name] = self.workspaces.pop(old_name)
85 self._write_file()
86
87 @lockutils.synchronized('workspaces', external=True)
88 def move_workspace(self, name, path):
89 self._populate()
90 path = os.path.abspath(os.path.expanduser(path))
91 self._name_exists(name)
92 self._validate_path(path)
93 self.workspaces[name] = path
94 self._write_file()
95
96 def _name_exists(self, name):
97 if name not in self.workspaces:
98 print("A workspace was not found with name: {0}".format(name))
99 sys.exit(1)
100
101 @lockutils.synchronized('workspaces', external=True)
102 def remove_workspace(self, name):
103 self._populate()
104 self._name_exists(name)
105 self.workspaces.pop(name)
106 self._write_file()
107
108 @lockutils.synchronized('workspaces', external=True)
109 def list_workspaces(self):
110 self._populate()
111 self._validate_workspaces()
112 return self.workspaces
113
114 def _workspace_name_exists(self, name):
115 if name in self.workspaces:
116 print("A workspace already exists with name: {0}.".format(
117 name))
118 sys.exit(1)
119
120 def _validate_path(self, path):
121 if not os.path.exists(path):
122 print("Path does not exist.")
123 sys.exit(1)
124
125 @lockutils.synchronized('workspaces', external=True)
126 def register_new_workspace(self, name, path, init=False):
127 """Adds the new workspace and writes out the new workspace config"""
128 self._populate()
129 path = os.path.abspath(os.path.expanduser(path))
130 # This only happens when register is called from outside of init
131 if not init:
132 self._validate_path(path)
133 self._workspace_name_exists(name)
134 self.workspaces[name] = path
135 self._write_file()
136
137 def _validate_workspaces(self):
138 if self.workspaces is not None:
139 self.workspaces = {n: p for n, p in self.workspaces.items()
140 if os.path.exists(p)}
141 self._write_file()
142
143 def _write_file(self):
144 with open(self.path, 'w') as f:
145 f.write(yaml.dump(self.workspaces))
146
147 def _populate(self):
148 if not os.path.isfile(self.path):
149 return
150 with open(self.path, 'r') as f:
151 self.workspaces = yaml.load(f) or {}
152
153
154class TempestWorkspace(command.Command):
155 def take_action(self, parsed_args):
156 self.manager = WorkspaceManager(parsed_args.workspace_path)
157 if getattr(parsed_args, 'register', None):
158 self.manager.register_new_workspace(
159 parsed_args.name, parsed_args.path)
160 elif getattr(parsed_args, 'rename', None):
161 self.manager.rename_workspace(
162 parsed_args.old_name, parsed_args.new_name)
163 elif getattr(parsed_args, 'move', None):
164 self.manager.move_workspace(
165 parsed_args.name, parsed_args.path)
166 elif getattr(parsed_args, 'remove', None):
167 self.manager.remove_workspace(
168 parsed_args.name)
169 else:
170 self._print_workspaces()
171 sys.exit(0)
172
173 def get_description(self):
174 return 'Tempest workspace actions'
175
176 def get_parser(self, prog_name):
177 parser = super(TempestWorkspace, self).get_parser(prog_name)
178
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
184 subparsers = parser.add_subparsers()
185
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900186 list_parser = subparsers.add_parser(
187 'list', help='Outputs the name and path of all known tempest '
188 'workspaces')
step682980c14ec2016-02-23 14:53:52 -0500189 list_parser.set_defaults(list=True)
190
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900191 register_parser = subparsers.add_parser(
192 'register', help='Registers a new tempest workspace via a given '
193 '--name and --path')
step682980c14ec2016-02-23 14:53:52 -0500194 register_parser.add_argument('--name', required=True)
195 register_parser.add_argument('--path', required=True)
196 register_parser.set_defaults(register=True)
197
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900198 update_parser = subparsers.add_parser(
199 'rename', help='Renames a tempest workspace from --old-name to '
200 '--new-name')
step682980c14ec2016-02-23 14:53:52 -0500201 update_parser.add_argument('--old-name', required=True)
202 update_parser.add_argument('--new-name', required=True)
203 update_parser.set_defaults(rename=True)
204
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900205 move_parser = subparsers.add_parser(
206 'move', help='Changes the path of a given tempest workspace '
207 '--name to --path')
step682980c14ec2016-02-23 14:53:52 -0500208 move_parser.add_argument('--name', required=True)
209 move_parser.add_argument('--path', required=True)
210 move_parser.set_defaults(move=True)
211
Masayuki Igawa043c7d72016-06-24 14:26:32 +0900212 remove_parser = subparsers.add_parser(
213 'remove', help='Deletes the entry for a given tempest workspace '
214 '--name')
step682980c14ec2016-02-23 14:53:52 -0500215 remove_parser.add_argument('--name', required=True)
216 remove_parser.set_defaults(remove=True)
217
218 return parser
219
220 def _print_workspaces(self):
221 output = prettytable.PrettyTable(["Name", "Path"])
222 if self.manager.list_workspaces() is not None:
223 for name, path in self.manager.list_workspaces().items():
224 output.add_row([name, path])
225
226 print(output)