Make server url into a user inputted config param
[dcaegen2/platform/cli.git] / dcae-cli / dcae_cli / util / profiles.py
1 # ============LICENSE_START=======================================================
2 # org.onap.dcae
3 # ================================================================================
4 # Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
5 # ================================================================================
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #      http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 # ============LICENSE_END=========================================================
18 #
19 # ECOMP is a trademark and service mark of AT&T Intellectual Property.
20
21 #!/usr/bin/env python3
22 # -*- coding: utf-8 -*-
23 """
24 Provides dcae cli profile variables
25 """
26 import os
27 from collections import namedtuple
28
29 import six
30
31 from dcae_cli import util
32 from dcae_cli.util import get_app_dir, get_pref, write_pref
33 from dcae_cli.util import config
34 from dcae_cli.util.config import get_config, update_config
35 from dcae_cli.util.exc import DcaeException
36 from dcae_cli.util.logger import get_logger
37
38
39 logger = get_logger('Profile')
40
41
42 # reserved profile names
43 ACTIVE = 'active'
44 _reserved_names = {ACTIVE}
45
46
47 # create enums for profile keys so that they can be imported for testing, instead of using literals
48 CONSUL_HOST = 'consul_host'
49 CONFIG_BINDING_SERVICE = 'config_binding_service'
50 CDAP_BROKER = 'cdap_broker'
51 DOCKER_HOST = 'docker_host'
52
53 # TODO: Should probably lift this strict list of allowed keys and repurpose to be
54 # keys that are required.
55 _allowed_keys = set([CONSUL_HOST, CONFIG_BINDING_SERVICE, CDAP_BROKER, DOCKER_HOST])
56 Profile = namedtuple('Profile', _allowed_keys)
57
58
59 def _create_stub_profile():
60     """Create a new stub of a profile"""
61     return { k: "" for k in _allowed_keys }
62
63
64 def _fmt_seq(seq):
65     '''Returns a sorted string formatted list'''
66     return list(sorted(map(str, seq)))
67
68
69 def get_profiles_path():
70     '''Returns the absolute path to the profiles file'''
71     return os.path.join(get_app_dir(), 'profiles.json')
72
73
74 def get_active_name():
75     '''Returns the active profile name in the config'''
76     return config.get_active_profile()
77
78
79 def _set_active_name(name):
80     '''Sets the active profile name in the config'''
81     update_config(active_profile=name)
82
83
84 class ProfilesInitError(RuntimeError):
85     pass
86
87 def reinit_profiles():
88     """Reinitialize profiles
89
90     Grab the remote profiles and merge with the local profiles if there is one.
91
92     Returns:
93     --------
94     Dict of complete new profiles
95     """
96     # Grab the remote profiles and merge it in
97     try:
98         server_url = config.get_server_url()
99         new_profiles = util.fetch_file_from_web(server_url, "/dcae-cli/profiles.json")
100     except:
101         # REVIEW: Should we allow users to manually setup their config if not
102         # able to pull from remote server?
103         raise ProfilesInitError("Could not download profiles from remote server")
104
105     profiles_path = get_profiles_path()
106
107     if  util.pref_exists(profiles_path):
108         existing_profiles = get_profiles(include_active=False)
109         # Make sure to clobber existing values and not other way
110         existing_profiles.update(new_profiles)
111         new_profiles = existing_profiles
112
113     write_pref(new_profiles, profiles_path)
114     return new_profiles
115
116
117 def get_profiles(user_only=False, include_active=True):
118     '''Returns a dict containing all available profiles
119
120     Example of the returned dict:
121         {
122             "profile-foo": {
123                 "some_variable_A": "some_value_A",
124                 "some_variable_B": "some_value_B",
125                 "some_variable_C": "some_value_C"
126             }
127         }
128     '''
129     try:
130         profiles = get_pref(get_profiles_path(), reinit_profiles)
131     except ProfilesInitError as e:
132         raise DcaeException("Failed to initialize profiles: {0}".format(e))
133
134     if user_only:
135         return profiles
136
137     if include_active:
138         active_name = get_active_name()
139         if active_name not in profiles:
140             raise DcaeException("Active profile '{}' does not exist. How did this happen?".format(active_name))
141         profiles[ACTIVE] = profiles[active_name]
142
143     return profiles
144
145
146 def get_profile(name=ACTIVE):
147     '''Returns a `Profile` object'''
148     profiles = get_profiles()
149
150     if name not in profiles:
151         raise DcaeException("Specified profile '{}' does not exist.".format(name))
152
153     try:
154         profile = Profile(**profiles[name])
155     except TypeError as e:
156         raise DcaeException("Specified profile '{}' is malformed.".format(name))
157
158     return profile
159
160
161 def create_profile(name, **kwargs):
162     '''Creates a new profile'''
163     _assert_not_reserved(name)
164
165     profiles = get_profiles(user_only=True)
166     if name in profiles:
167         raise DcaeException("Profile '{}' already exists.".format(name))
168
169     profile = _create_stub_profile()
170     profile.update(kwargs)
171     _assert_valid_profile(profile)
172
173     profiles[name] = profile
174     _write_profiles(profiles)
175
176
177 def delete_profile(name):
178     '''Deletes a profile'''
179     _assert_not_reserved(name)
180     profiles = get_profiles(user_only=True)
181     if name not in profiles:
182         raise DcaeException("Profile '{}' does not exist.".format(name))
183     if name == get_active_name():
184         logger.warning("Profile '{}' is currently active. Activate another profile first."
185                 .format(name))
186         return False
187     del profiles[name]
188     _write_profiles(profiles)
189     return True
190
191
192 def update_profile(name, **kwargs):
193     '''Creates or updates a profile'''
194     _assert_not_reserved(name)
195     _assert_valid_profile(kwargs)
196
197     profiles = get_profiles(user_only=True)
198     if name not in profiles:
199         raise DcaeException("Profile '{}' does not exist.".format(name))
200
201     profiles[name].update(kwargs)
202     _write_profiles(profiles)
203
204
205 def _assert_valid_profile(params):
206     '''Raises DcaeException if the profile parameter dict is invalid'''
207     if not params:
208         raise DcaeException('No update key-value pairs were provided.')
209     keys = set(params.keys())
210     if not _allowed_keys.issuperset(keys):
211         invalid_keys = keys - _allowed_keys
212         raise DcaeException("Invalid keys {} detected. Only keys {} are supported.".format(_fmt_seq(invalid_keys), _fmt_seq(_allowed_keys)))
213
214
215 def _assert_not_reserved(name):
216     '''Raises DcaeException if the profile is reserved'''
217     if name in _reserved_names:
218         raise DcaeException("Profile '{}' is reserved and cannot be modified.".format(name))
219
220
221 def _write_profiles(profiles):
222     '''Writes the profiles dictionary to disk'''
223     return write_pref(profiles, path=get_profiles_path())
224
225
226 def activate_profile(name):
227     '''Modifies the config and sets a new active profile'''
228     avail_profiles = set(get_profiles().keys()) - {ACTIVE, }
229     if name not in avail_profiles:
230         raise DcaeException("Profile name '{}' does not exist. Please select from {} or create a new profile.".format(name, _fmt_seq(avail_profiles)))
231     _set_active_name(name)