juju_scaleway.Environment.add_machine()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
dl 0
loc 8
rs 9.4286
1
# -*- coding: utf-8 -*-
2
#
3
# Copyright (c) 2014-2015 Online SAS and Contributors. All Rights Reserved.
4
#                         Edouard Bonlieu <[email protected]>
5
#                         Julien Castets <[email protected]>
6
#                         Manfred Touron <[email protected]>
7
#                         Kevin Deldycke <[email protected]>
8
#
9
# Licensed under the BSD 2-Clause License (the "License"); you may not use this
10
# file except in compliance with the License. You may obtain a copy of the
11
# License at http://opensource.org/licenses/BSD-2-Clause
12
13
try:
14
    import httplib
15
except ImportError:  # Python3
16
    import http.client as httplib
17
18
import logging
19
import shutil
20
import subprocess
21
import socket
22
23
import os
24
import yaml
25
26
from juju_scaleway.constraints import SERIES_MAP
27
28
29
logger = logging.getLogger("juju.scaleway")
30
31
32
class Environment(object):
33
34
    def __init__(self, config):
35
        self.config = config
36
37
    def _run(self, command, env=None, capture_err=False):
38
        if env is None:
39
            env = dict(os.environ)
40
        env["JUJU_ENV"] = self.config.get_env_name()
41
        args = ['juju']
42
        args.extend(command)
43
        logger.debug("Running juju command: %s", " ".join(args))
44
        try:
45
            if capture_err:
46
                return subprocess.check_call(
47
                    args, env=env, stderr=subprocess.STDOUT)
48
            return subprocess.check_output(
49
                args, env=env, stderr=subprocess.STDOUT)
50
        except subprocess.CalledProcessError as exc:
51
            logger.error(
52
                "Failed to run command %s\n%s",
53
                ' '.join(args), exc.output
54
            )
55
            raise
56
57
    def status(self):
58
        return yaml.safe_load(self._run(['status']))
59
60
    def is_running(self):
61
        """Try to connect the api server websocket to see if env is running.
62
        """
63
        name = self.config.get_env_name()
64
        jenv = os.path.join(
65
            self.config.juju_home, "environments", "%s.jenv" % name)
66
        if not os.path.exists(jenv):
67
            return False
68
        with open(jenv) as handle:
69
            data = yaml.safe_load(handle.read())
70
            if not data:
71
                return False
72
            conf = data.get('bootstrap-config')
73
            if not conf['type'] in ('manual', 'null'):
74
                return False
75
        conn = httplib.HTTPSConnection(
76
            conf['bootstrap-host'], port=17070, timeout=1.2)
77
        try:
78
            conn.request("GET", "/")
79
            return True
80
        except socket.error:
81
            return False
82
83
    def add_machine(self, location, key=None, debug=False):
84
        ops = ['add-machine', location]
85
        if key:
86
            ops.extend(['--ssh-key', key])
87
        if debug:
88
            ops.append('--debug')
89
90
        return self._run(ops, capture_err=debug)
91
92
    def terminate_machines(self, machines):
93
        cmd = ['terminate-machine', '--force']
94
        cmd.extend(machines)
95
        return self._run(cmd)
96
97
    def destroy_environment(self):
98
        cmd = [
99
            'destroy-environment', "-y", self.config.get_env_name()]
100
        return self._run(cmd)
101
102
    def destroy_environment_jenv(self):
103
        """Force remove client cache of environment by deleting jenv.
104
        Specifically this is for when we force/fast destroy an environment
105
        by deleting all the underlying iaas resources. Juju cli with --force
106
        will work, but will wait for a timeout to connect to the state server
107
        before doing the same.
108
        """
109
        env_name = self.config.get_env_name()
110
        jenv_path = os.path.join(
111
            self.config.juju_home, "environments", "%s.jenv" % env_name)
112
        if os.path.exists(jenv_path):
113
            os.remove(jenv_path)
114
115
    def bootstrap(self):
116
        return self._run(['bootstrap', '-v'])
117
118
    def bootstrap_jenv(self, host):
119
        """Bootstrap an environment in a sandbox.
120
        Manual provider config keeps transient state in the form of
121
        bootstrap-host for its config.
122
        A temporary JUJU_HOME is used to modify environments.yaml
123
        """
124
        env_name = self.config.get_env_name()
125
126
        # Prep a new juju home
127
        boot_home = os.path.join(
128
            self.config.juju_home, "boot-%s" % env_name)
129
130
        if not os.path.exists(boot_home):
131
            os.makedirs(os.path.join(boot_home, 'environments'))
132
133
        # Check that this installation has been used before.
134
        jenv_dir = os.path.join(self.config.juju_home, 'environments')
135
        if not os.path.exists(jenv_dir):
136
            os.mkdir(jenv_dir)
137
138
        ssh_key_dir = os.path.join(self.config.juju_home, 'ssh')
139
140
        # If no keys, create juju ssh keys via side effect.
141
        if not os.path.exists(ssh_key_dir):
142
            self._run(["switch"])
143
144
        # Use existing juju ssh keys when bootstrapping
145
        shutil.copytree(
146
            ssh_key_dir,
147
            os.path.join(boot_home, 'ssh'))
148
149
        # Updated env config with the bootstrap host.
150
        with open(self.config.get_env_conf()) as handle:
151
            data = yaml.safe_load(handle.read())
152
            env_conf = data['environments'].get(env_name)
153
        env_conf['bootstrap-host'] = host
154
155
        with open(os.path.join(boot_home, 'environments.yaml'), 'w') as handle:
156
            handle.write(yaml.safe_dump({
157
                'environments': {env_name: env_conf}
158
            }))
159
160
        # Change JUJU_ENV
161
        env = dict(os.environ)
162
        env['JUJU_HOME'] = boot_home
163
        env['JUJU_LOGGING'] = "<root>=DEBUG"
164
        cmd = ['bootstrap', '--debug']
165
        if self.config.upload_tools:
166
            cmd.append("--upload-tools")
167
            cmd.append('--series')
168
            cmd.append("%s" % (",".join(sorted(SERIES_MAP.values()))))
169
170
        capture_err = self.config.verbose and True or False
171
        try:
172
            self._run(cmd, env=env, capture_err=capture_err)
173
            # Copy over the jenv
174
            shutil.copy(
175
                os.path.join(
176
                    boot_home, "environments", "%s.jenv" % env_name),
177
                os.path.join(
178
                    self.config.juju_home,
179
                    "environments", "%s.jenv" % env_name))
180
        finally:
181
            shutil.rmtree(boot_home)
182