~juju-deployers/juju-deployer/darwin

« back to all changes in this revision

Viewing changes to deployer/cli.py

  • Committer: Adam Gandelman
  • Date: 2013-09-03 20:44:14 UTC
  • mfrom: (114 darwin)
  • mto: This revision was merged to the branch mainline in revision 119.
  • Revision ID: adamg@canonical.com-20130903204414-xsqqz2gp83dp5d2o
MergeĀ lp:juju-deployer/darwin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
"""
 
3
Juju Deployer
 
4
 
 
5
Deployment automation for juju.
 
6
 
 
7
"""
 
8
 
 
9
 
 
10
import argparse
 
11
import logging
 
12
import os
 
13
import sys
 
14
import time
 
15
 
 
16
from deployer.config import ConfigStack
 
17
from deployer.env import select_runtime
 
18
from deployer.action import diff, importer
 
19
from deployer.utils import ErrorExit, setup_logging
 
20
 
 
21
 
 
22
def setup_parser():
 
23
    parser = argparse.ArgumentParser()
 
24
    parser.add_argument(
 
25
        '-c', '--config',
 
26
        help=('File containing deployment(s) json config. This '
 
27
              'option can be repeated, with later files overriding '
 
28
              'values in earlier ones.'),
 
29
        dest='configs', action='append')
 
30
    parser.add_argument(
 
31
        '-d', '--debug', help='Enable debugging to stdout',
 
32
        dest="debug",
 
33
        action="store_true", default=False)
 
34
    parser.add_argument(
 
35
        '-L', '--local-mods',
 
36
        help='Disallow deployment of locally-modified charms',
 
37
        dest="no_local_mods", default=True, action='store_false')
 
38
    parser.add_argument(
 
39
        '-u', '--update-charms',
 
40
        help='Update existing charm branches',
 
41
        dest="update_charms", default=False, action="store_true")
 
42
    parser.add_argument(
 
43
        '-l', '--ls', help='List available deployments',
 
44
        dest="list_deploys", action="store_true", default=False)
 
45
    parser.add_argument(
 
46
        '-D', '--destroy-services',
 
47
        help='Destroy all services (do not terminate machines)',
 
48
        dest="destroy_services", action="store_true",
 
49
        default=False)
 
50
    parser.add_argument(
 
51
        '-T', '--terminate-machines',
 
52
        help=('Terminate all machines but the bootstrap node.  '
 
53
              'Destroy any services that exist on each'),
 
54
        dest="terminate_machines", action="store_true",
 
55
        default=False)
 
56
    parser.add_argument(
 
57
        '-t', '--timeout',
 
58
        help='Timeout (sec) for entire deployment (45min default)',
 
59
        dest='timeout', action='store', type=int, default=2700)
 
60
    parser.add_argument(
 
61
        "-f", '--find-service', action="store", type=str,
 
62
        help='Find hostname from first unit of a specific service.',
 
63
        dest="find_service")
 
64
    parser.add_argument(
 
65
        "-b", '--branch-only', action="store_true",
 
66
        help='Update vcs branches and exit.',
 
67
        dest="branch_only")
 
68
    parser.add_argument(
 
69
        '-s', '--deploy-delay', action='store', type=float,
 
70
        help=("Time in seconds to sleep between 'deploy' commands, "
 
71
              "to allow machine provider to process requests. On "
 
72
              "terminate machines this also signals waiting for "
 
73
              "machine removal."),
 
74
        dest="deploy_delay", default=0)
 
75
    parser.add_argument(
 
76
        '-e', '--environment', action='store', dest='juju_env',
 
77
        help='Deploy to a specific Juju environment.',
 
78
        default=os.getenv('JUJU_ENV'))
 
79
    parser.add_argument(
 
80
        '-o', '--override', action='append', type=str,
 
81
        help=('Override *all* config options of the same name '
 
82
              'across all services.  Input as key=value.'),
 
83
        dest='overrides', default=None)
 
84
    parser.add_argument(
 
85
        '-v', '--verbose', action='store_true', default=False,
 
86
        dest="verbose", help='Verbose output')
 
87
    parser.add_argument(
 
88
        '-W', '--watch', help='Watch environment changes on console',
 
89
        dest="watch", action="store_true", default=False)
 
90
    parser.add_argument(
 
91
        '-r', "--retry", default=0, type=int, dest="retry_count",
 
92
        help=("Resolve CLI and unit errors via number of retries (default: 0)."
 
93
              " Either standalone or in a deployment"))
 
94
    parser.add_argument(
 
95
        "--diff", action="store_true", default=False,
 
96
        help=("Generate a delta between a configured deployment and a running"
 
97
             " environment."))
 
98
    parser.add_argument(
 
99
        '-w', '--relation-wait', action='store', dest='rel_wait',
 
100
        default=60, type=int,
 
101
        help=('Number of seconds to wait before checking for '
 
102
              'relation errors after all relations have been added '
 
103
              'and subordinates started. (default: 60)'))
 
104
    parser.add_argument("--description", help=argparse.SUPPRESS,
 
105
                        action="store_true")
 
106
    parser.add_argument("deployment", nargs="?")
 
107
    return parser
 
108
 
 
109
 
 
110
def main():
 
111
    stime = time.time()
 
112
    try:
 
113
        run()
 
114
    except ErrorExit:
 
115
        logging.getLogger('deployer.cli').info(
 
116
            "Deployment stopped. run time: %0.2f", time.time() - stime)
 
117
        sys.exit(1)
 
118
 
 
119
 
 
120
def run():
 
121
    parser = setup_parser()
 
122
    options = parser.parse_args()
 
123
 
 
124
    if options.description:
 
125
        print "Tool for declarative management of complex deployments."
 
126
        sys.exit(0)
 
127
 
 
128
    # Debug implies watching and verbose
 
129
    if options.debug:
 
130
        options.watch = options.verbose = True
 
131
    setup_logging(options.verbose, options.debug)
 
132
 
 
133
    log = logging.getLogger("deployer.cli")
 
134
    start_time = time.time()
 
135
 
 
136
    env = select_runtime(options.juju_env, options)
 
137
    log.debug('Using runtime %s', env.__class__.__name__)
 
138
 
 
139
    config = ConfigStack(options.configs or [])
 
140
 
 
141
    # Destroy services and exit
 
142
    if options.destroy_services or options.terminate_machines:
 
143
        log.info("Resetting environment...")
 
144
        env.connect()
 
145
        env.reset(terminate_machines=options.terminate_machines,
 
146
                  terminate_delay=options.deploy_delay,
 
147
                  watch=options.watch)
 
148
        log.info("Environment reset in %0.2f", time.time() - start_time)
 
149
        sys.exit(0)
 
150
 
 
151
    # Display service info and exit
 
152
    if options.find_service:
 
153
        address = env.get_service_address(options.find_service)
 
154
        if address is None:
 
155
            log.error("Service not found %r", options.find_service)
 
156
            sys.exit(1)
 
157
        elif not address:
 
158
            log.warning("Service: %s has no address for first unit",
 
159
                        options.find_service)
 
160
        else:
 
161
            log.info("Service: %s address: %s", options.find_service, address)
 
162
            print address
 
163
        sys.exit(0)
 
164
 
 
165
    # Just resolve/retry hooks in the environment
 
166
    if not options.deployment and options.retry_count:
 
167
        log.info("Retrying hooks for error resolution")
 
168
        env.connect()
 
169
        env.resolve_errors(
 
170
            options.retry_count, watch=options.watch, timeout=options.timeout)
 
171
 
 
172
    # Arg check on config files and deployment name.
 
173
    if not options.configs:
 
174
        log.error("Config files must be specified")
 
175
        sys.exit(1)
 
176
 
 
177
    config.load()
 
178
 
 
179
    # Just list the available deployments
 
180
    if options.list_deploys:
 
181
        print "\n".join(sorted(config.keys()))
 
182
        sys.exit(0)
 
183
 
 
184
    # Do something to a deployment
 
185
    if not options.deployment:
 
186
        log.error(
 
187
            "Deployment name must be specified. available: %s", tuple(
 
188
                sorted(config.keys())))
 
189
        sys.exit(1)
 
190
 
 
191
    deployment = config.get(options.deployment)
 
192
 
 
193
    if options.diff:
 
194
        diff.Diff(env, deployment, options).run()
 
195
        return
 
196
 
 
197
    # Import it
 
198
    log.info("Starting deployment of %s", options.deployment)
 
199
    importer.Importer(env, deployment, options).run()
 
200
 
 
201
    # Deploy complete
 
202
    log.info("Deployment complete in %0.2f seconds" % (
 
203
        time.time() - start_time))
 
204
 
 
205
 
 
206
if __name__ == '__main__':
 
207
    main()