136
def get_token_from_status(client):
137
"""Return the token from the application status message or None."""
138
status = client.get_status()
139
unit = status.get_unit('dummy-sink/0')
140
app_status = unit.get('workload-status')
141
if app_status is not None:
142
message = app_status.get('message', '')
143
parts = message.split()
149
127
def check_token(client, token, timeout=120):
150
"""Check the token found on dummy-sink/0 or raise ValueError."""
151
logging.info('Waiting for applications to reach ready.')
152
client.wait_for_workloads()
153
128
# Wait up to 120 seconds for token to be created.
129
# Utopic is slower, maybe because the devel series gets more
154
131
logging.info('Retrieving token.')
155
132
remote = remote_from_unit(client, "dummy-sink/0")
156
133
# Update remote with real address if needed.
158
135
start = time.time()
160
137
if remote.is_windows():
161
result = get_token_from_status(client)
164
result = remote.cat("%ProgramData%\\dummy-sink\\token")
165
except winrm.exceptions.WinRMTransportError as e:
167
"Skipping token check because of: {}".format(str(e)))
139
result = remote.cat("%ProgramData%\\dummy-sink\\token")
140
except winrm.exceptions.WinRMTransportError as e:
141
print("Skipping token check because of: {}".format(str(e)))
170
143
result = remote.run(GET_TOKEN_SCRIPT)
171
144
token_pattern = re.compile(r'([^\n\r]*)\r?\n?')
296
269
"""Compress log files in given log_dir using gzip."""
298
271
for r, ds, fs in os.walk(log_dir):
299
log_files.extend(os.path.join(r, f) for f in fs if is_log(f))
272
log_files.extend(os.path.join(r, f) for f in fs if f.endswith(".log"))
301
274
subprocess.check_call(['gzip', '--best', '-f'] + log_files)
304
def is_log(file_name):
305
"""Check to see if the given file name is the name of a log file."""
306
return file_name.endswith('.log') or file_name.endswith('syslog')
309
277
lxc_template_glob = '/var/lib/juju/containers/juju-*-lxc-template/*.log'
378
337
def assess_juju_run(client):
379
responses = client.run(('uname',), ['dummy-source', 'dummy-sink'])
338
responses = client.get_juju_output('run', '--format', 'json', '--service',
339
'dummy-source,dummy-sink', 'uname')
340
responses = json.loads(responses)
380
341
for machine in responses:
381
342
if machine.get('ReturnCode', 0) != 0:
382
343
raise ValueError('juju run on machine %s returned %d: %s' % (
392
353
def assess_upgrade(old_client, juju_path):
393
all_clients = _get_clients_to_upgrade(old_client, juju_path)
395
# all clients have the same provider type, work this out once.
396
if all_clients[0].env.config['type'] == 'maas':
354
client = EnvJujuClient.by_version(old_client.env, juju_path,
357
if client.env.config['type'] == 'maas':
401
for client in all_clients:
403
client.wait_for_version(client.get_matching_agent_version(), timeout)
406
def _get_clients_to_upgrade(old_client, juju_path):
407
"""Return a list of cloned clients to upgrade.
409
Ensure that the controller (if available) is the first client in the list.
411
new_client = old_client.clone_path_cls(juju_path)
412
all_clients = sorted(
413
new_client.iter_model_clients(),
414
key=lambda m: m.model_name == 'controller',
361
client.wait_for_version(client.get_matching_agent_version(), timeout)
420
364
def upgrade_juju(client):
421
client.set_testing_agent_metadata_url()
422
tools_metadata_url = client.get_agent_metadata_url()
424
'The {url_type} is {url}'.format(
425
url_type=client.agent_metadata_url,
426
url=tools_metadata_url))
365
client.set_testing_tools_metadata_url()
366
tools_metadata_url = client.get_env_option('tools-metadata-url')
367
logging.info('The tools-metadata-url is %s', tools_metadata_url)
427
368
client.upgrade_juju()
536
477
self.known_hosts['0'] = bootstrap_host
539
def _generate_default_clean_dir(cls, temp_env_name):
540
"""Creates a new unique directory for logging and returns name"""
541
logging.info('Environment {}'.format(temp_env_name))
542
test_name = temp_env_name.split('-')[0]
543
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
544
log_dir = os.path.join('/tmp', test_name, 'logs', timestamp)
548
logging.info('Created logging directory {}'.format(log_dir))
550
if e.errno == errno.EEXIST:
551
logging.warn('"Directory {} already exists'.format(log_dir))
553
raise('Failed to create logging directory: {} ' +
555
'. Please specify empty folder or try again')
559
480
def from_args(cls, args):
561
args.logs = cls._generate_default_clean_dir(args.temp_env_name)
563
# GZ 2016-08-11: Move this logic into client_from_config maybe?
564
if args.juju_bin == 'FAKE':
565
env = SimpleEnvironment.from_config(args.env)
566
client = fake_juju_client(env=env)
568
client = client_from_config(args.env, args.juju_bin,
570
soft_deadline=args.deadline)
481
env = SimpleEnvironment.from_config(args.env)
482
client = EnvJujuClient.by_version(env, args.juju_bin, debug=args.debug)
571
483
jes_enabled = client.is_jes_enabled()
573
485
args.temp_env_name, client, client, args.bootstrap_host,
646
564
raise AssertionError('Tear down client needs same env!')
647
565
tear_down(self.tear_down_client, jes_enabled, try_jes=try_jes)
649
def _log_and_wrap_exception(self, exc):
650
logging.exception(exc)
651
stdout = getattr(exc, 'output', None)
652
stderr = getattr(exc, 'stderr', None)
655
'Output from exception:\nstdout:\n%s\nstderr:\n%s',
657
return LoggedException(exc)
660
568
def bootstrap_context(self, machines, omit_config=None):
661
569
"""Context for bootstrapping a state server."""
695
603
ensure_deleted(jenv_path)
696
604
with temp_bootstrap_env(self.client.env.juju_home, self.client,
697
605
permanent=self.permanent, set_home=False):
698
with self.handle_bootstrap_exceptions():
700
self.tear_down(try_jes=True)
704
def existing_bootstrap_context(self, machines, omit_config=None):
705
""" Context for bootstrapping a state server that shares the
706
environment with an existing bootstrap environment.
708
Using this context makes it possible to boot multiple simultaneous
709
environments that share a JUJU_HOME.
712
bootstrap_host = self.known_hosts.get('0')
714
series=self.series, bootstrap_host=bootstrap_host,
715
agent_url=self.agent_url, agent_stream=self.agent_stream,
717
if omit_config is not None:
718
for key in omit_config:
719
kwargs.pop(key.replace('-', '_'), None)
720
update_env(self.client.env, self.temp_env_name, **kwargs)
721
ssh_machines = list(machines)
722
if bootstrap_host is not None:
723
ssh_machines.append(bootstrap_host)
724
for machine in ssh_machines:
725
logging.info('Waiting for port 22 on %s' % machine)
726
wait_for_port(machine, 22, timeout=120)
728
with self.handle_bootstrap_exceptions():
732
def handle_bootstrap_exceptions(self):
733
"""If an exception is raised during bootstrap, handle it.
735
Log the exception, re-raise as a LoggedException.
736
Copy logs for the bootstrap host
737
Tear down. (self.keep_env is ignored.)
742
# If an exception is raised that indicates an error, log it
743
# before tearing down so that the error is closely tied to
744
# the failed operation.
745
except Exception as e:
746
raise self._log_and_wrap_exception(e)
748
# If run from a windows machine may not have ssh to get
750
with self.client.ignore_soft_deadline():
751
with self.tear_down_client.ignore_soft_deadline():
752
if self.bootstrap_host is not None and _can_run_ssh():
753
remote = remote_from_address(self.bootstrap_host,
755
copy_remote_logs(remote, self.log_dir)
756
archive_logs(self.log_dir)
609
self.tear_down(try_jes=True)
611
# If an exception is raised that indicates an error, log it
612
# before tearing down so that the error is closely tied to
613
# the failed operation.
614
except Exception as e:
616
if getattr(e, 'output', None):
619
raise LoggedException(e)
621
# If run from a windows machine may not have ssh to get
623
if self.bootstrap_host is not None and _can_run_ssh():
624
remote = remote_from_address(self.bootstrap_host,
626
copy_remote_logs(remote, self.log_dir)
627
archive_logs(self.log_dir)
761
632
def runtime_context(self, addable_machines):
779
650
except GeneratorExit:
781
652
except BaseException as e:
782
raise self._log_and_wrap_exception(e)
654
raise LoggedException(e)
784
656
safe_print_status(self.client)
787
with self.client.ignore_soft_deadline():
788
self.client.list_controllers()
789
self.client.list_models()
790
for m_client in self.client.iter_model_clients():
791
m_client.show_status()
659
self.client.show_status()
793
with self.client.ignore_soft_deadline():
794
with self.tear_down_client.ignore_soft_deadline():
797
except KeyboardInterrupt:
799
if not self.keep_env:
800
self.tear_down(self.jes_enabled)
802
# GZ 2016-08-11: Should this method be elsewhere to avoid poking backend?
803
def _should_dump(self):
804
return not isinstance(self.client._backend, FakeBackend)
663
except KeyboardInterrupt:
665
if not self.keep_env:
666
self.tear_down(self.jes_enabled)
806
668
def dump_all_logs(self):
807
669
"""Dump logs for all models in the bootstrapped controller."""
808
670
# This is accurate because we bootstrapped self.client. It might not
809
671
# be accurate for a model created by create_environment.
810
if not self._should_dump():
812
controller_client = self.client.get_controller_client()
672
admin_client = self.client.get_admin_client()
813
673
if not self.jes_enabled:
814
674
clients = [self.client]
817
677
clients = list(self.client.iter_model_clients())
818
678
except Exception:
819
679
# Even if the controller is unreachable, we may still be able
820
# to gather some logs. The controller_client and self.client
821
# instances are all we have knowledge of.
822
clients = [controller_client]
823
if self.client is not controller_client:
680
# to gather some logs. admin_client and self.client are all
681
# we have knowledge of.
682
clients = [admin_client]
683
if self.client is not admin_client:
824
684
clients.append(self.client)
825
685
for client in clients:
826
if client.env.environment == controller_client.env.environment:
686
if client.env.environment == admin_client.env.environment:
827
687
known_hosts = self.known_hosts
828
688
if self.jes_enabled:
829
689
runtime_config = self.client.get_cache_path()
844
704
"""Context for running all juju operations in."""
845
705
with self.maas_machines() as machines:
846
706
with self.aws_machines() as new_machines:
848
yield machines + new_machines
850
# This is not done in dump_all_logs because it should be
851
# done after tear down.
852
if self.log_dir is not None:
853
dump_juju_timings(self.client, self.log_dir)
707
yield machines + new_machines
856
def booted_context(self, upload_tools, **kwargs):
710
def booted_context(self, upload_tools):
857
711
"""Create a temporary environment in a context manager to run tests in.
859
713
Bootstrap a new environment from a temporary config that is suitable
867
721
:param upload_tools: False or True to upload the local agent instead
868
722
of using streams.
869
:param **kwargs: All remaining keyword arguments are passed to the
873
725
with self.top_context() as machines:
874
726
with self.bootstrap_context(
875
727
machines, omit_config=self.client.bootstrap_replaces):
876
728
self.client.bootstrap(
877
upload_tools=upload_tools,
878
bootstrap_series=self.series,
880
with self.runtime_context(machines):
881
self.client.list_controllers()
882
self.client.list_models()
883
for m_client in self.client.iter_model_clients():
884
m_client.show_status()
886
except LoggedException:
890
def existing_booted_context(self, upload_tools, **kwargs):
892
with self.top_context() as machines:
893
# Existing does less things as there is no pre-cleanup needed.
894
with self.existing_bootstrap_context(
895
machines, omit_config=self.client.bootstrap_replaces):
896
self.client.bootstrap(
897
upload_tools=upload_tools,
898
bootstrap_series=self.series,
729
upload_tools, bootstrap_series=self.series)
900
730
with self.runtime_context(machines):
902
732
except LoggedException:
951
781
sys.path = [p for p in sys.path if 'OpenSSH' not in p]
952
782
# GZ 2016-01-22: When upgrading, could make sure to tear down with the
953
783
# newer client instead, this will be required for major version upgrades?
954
client = client_from_config(args.env, start_juju_path, args.debug,
955
soft_deadline=args.deadline)
784
client = EnvJujuClient.by_version(
785
SimpleEnvironment.from_config(args.env), start_juju_path, args.debug)
956
786
if args.jes and not client.is_jes_enabled():
957
787
client.enable_jes()
958
788
jes_enabled = client.is_jes_enabled()
989
820
def safe_print_status(client):
990
821
"""Show the output of juju status without raising exceptions."""
992
for m_client in client.iter_model_clients():
993
m_client.show_status()
994
824
except Exception as e:
995
825
logging.exception(e)
998
def wait_for_state_server_to_shutdown(host, client, instance_id, timeout=60):
828
def wait_for_state_server_to_shutdown(host, client, instance_id):
999
829
print_now("Waiting for port to close on %s" % host)
1000
wait_for_port(host, 17070, closed=True, timeout=timeout)
830
wait_for_port(host, 17070, closed=True)
1001
831
print_now("Closed.")
1002
832
provider_type = client.env.config.get('type')
1003
833
if provider_type == 'openstack':