def _install_upstream_deb(self): log.info("Found upstream deb, installing that instead") filename = os.path.basename(self.config.getopt("upstream_deb")) try: Container.run( self.container_name, "dpkg -i /home/ubuntu/.cloud-install/{}".format(filename), output_cb=self.set_progress_output, ) except: # Make sure deps are installed if any new ones introduced by # the upstream packaging. Container.run(self.container_name, "apt-get install -qyf", output_cb=self.set_progress_output)
def _install_upstream_deb(self): log.info('Found upstream deb, installing that instead') filename = os.path.basename(self.config.getopt('upstream_deb')) try: Container.run( self.container_name, 'dpkg -i /home/ubuntu/.cloud-install/{}'.format(filename), output_cb=self.set_progress_output) except: # Make sure deps are installed if any new ones introduced by # the upstream packaging. Container.run(self.container_name, 'apt-get install -qyf', output_cb=self.set_progress_output)
def read_juju_log(self): try: return Container.run(self.container_name, 'tail -n 10 ' '/var/log/juju-ubuntu-local' '/all-machines.log') except Exception: return "Waiting..."
def cloud_init_finished(self, tries, maxlenient=20): """checks cloud-init result.json in container to find out status For the first `maxlenient` tries, it treats a container with no IP and SSH errors as non-fatal, assuming initialization is still ongoing. Afterwards, will raise exceptions for those errors, so as not to loop forever. returns True if cloud-init finished with no errors, False if it's not done yet, and raises an exception if it had errors. """ cmd = 'sudo cat /run/cloud-init/result.json' try: result_json = Container.run(self.container_name, cmd) except NoContainerIPException as e: log.debug("Container has no IPs according to lxc-info. " "Will retry.") return False except ContainerRunException as e: _, returncode = e.args if returncode == 255: if tries < maxlenient: log.debug("Ignoring initial SSH error.") return False raise e if returncode == 1: # the 'cat' did not find the file. if tries < 1: log.debug("Waiting for cloud-init status result") return False else: log.debug("Unexpected return code from reading " "cloud-init status in container.") raise e if result_json == '': return False try: ret = json.loads(result_json) except Exception as e: if tries < maxlenient + 10: log.debug("exception trying to parse '{}'" " - retrying".format(result_json)) return False log.error(str(e)) log.debug("exception trying to parse '{}'".format(result_json)) raise e errors = ret['v1']['errors'] if len(errors): log.error("Container cloud-init finished with " "errors: {}".format(errors)) raise Exception("Top-level container OS did not initialize " "correctly.") return True
def test_bootstrap_succeeded(self): """ Verifies a local bootstrap happened """ cmd = "JUJU_HOME=~/.cloud-install/juju juju stat --format yaml" out = Container.run(self.CONFIG["container_name"], cmd, use_ssh=True) out = out.split("\n")[0].strip() assert "environment: local" in out
def test_bootstrap_succeeded(self): """ Verifies a local bootstrap happened """ cmd = ("JUJU_HOME=~/.cloud-install/juju juju stat --format yaml") out = Container.run(self.CONFIG['container_name'], cmd, use_ssh=True) out = out.split("\n")[0].strip() assert ('environment: local' in out)
def read_cloud_init_output(self): try: s = Container.run(self.container_name, 'tail -n 10 ' '/var/log/cloud-init-output.log') return s.replace('\r', '') except Exception: return "Waiting..."
def test_container_ip_matches(self): """ Verifies container ip in config matches what LXC sees """ saved_ip = self.CONFIG['container_ip'] lxc_ip = Container.ip(self.CONFIG['container_name']) assert saved_ip == lxc_ip
def test_container_ip_matches(self): """ Verifies container ip in config matches what LXC sees """ saved_ip = self.CONFIG["container_ip"] lxc_ip = Container.ip(self.CONFIG["container_name"]) assert saved_ip == lxc_ip
def add_static_route(self, lxc_net): """ Adds static route to host system """ # Store container IP in config ip = Container.ip(self.container_name) self.config.setopt("container_ip", ip) log.info("Adding static route for {} via {}".format(lxc_net, ip)) out = utils.get_command_output("ip route add {} via {} dev lxcbr0".format(lxc_net, ip)) if out["status"] != 0: raise Exception("Could not add static route for {}" " network: {}".format(lxc_net, out["output"]))
def add_static_route(self, lxc_net): """ Adds static route to host system """ # Store container IP in config ip = Container.ip(self.container_name) self.config.setopt('container_ip', ip) log.info("Adding static route for {} via {}".format(lxc_net, ip)) out = utils.get_command_output( 'ip route add {} via {} dev lxcbr0'.format(lxc_net, ip)) if out['status'] != 0: raise Exception("Could not add static route for {}" " network: {}".format(lxc_net, out['output']))
def do_install(self): self.display_controller.status_info_message("Building environment") if os.path.exists(self.container_abspath): raise Exception("Container exists, please uninstall or kill " "existing cloud before proceeding.") # check for deb early, will actually install it later upstream_deb = self.config.getopt('upstream_deb') if upstream_deb and not os.path.isfile(upstream_deb): raise Exception("Upstream deb '{}' " "not found.".format(upstream_deb)) utils.ssh_genkey() self.setup_apt_proxy() self.prep_userdata() utils.render_charm_config(self.config) self.prep_juju() self.set_perms() self.create_container_and_wait() # Copy over host ssh keys Container.cp(self.container_name, os.path.join(utils.install_home(), '.ssh/id_rsa*'), '.ssh/.') # Install local copy of openstack installer if provided if upstream_deb: shutil.copy(upstream_deb, self.config.cfg_path) self._install_upstream_deb() # Stop before we attempt to access container if self.config.getopt('install_only'): log.info("Done installing, stopping here per --install-only.") self.config.setopt('install_only', True) self.loop.exit(0) # Update jujus no-proxy setting if applicable if self.config.getopt('http_proxy') or \ self.config.getopt('https_proxy'): log.info("Updating juju environments for proxy support") lxc_net = self.config.getopt('lxc_network') self.config.update_environments_yaml( key='no-proxy', val='{},localhost,{}'.format( Container.ip(self.container_name), netutils.get_ip_set(lxc_net))) # start the party cloud_status_bin = ['openstack-status'] self.tasker.start_task("Bootstrapping Juju", self.read_progress_output) Container.run(self.container_name, "{0} juju --debug bootstrap".format( self.config.juju_home(use_expansion=True)), use_ssh=True, output_cb=self.set_progress_output) Container.run( self.container_name, "{0} juju status".format( self.config.juju_home(use_expansion=True)), use_ssh=True) self.tasker.stop_current_task() self.display_controller.status_info_message( "Starting cloud deployment") Container.run_status( self.container_name, " ".join(cloud_status_bin), self.config)
def create_container_and_wait(self): """ Creates container and waits for cloud-init to finish """ self.tasker.start_task("Creating Container", self.read_container_status) Container.create(self.container_name, self.userdata) with open(os.path.join(self.container_abspath, 'fstab'), 'w') as f: f.write("{0} {1} none bind,create=dir\n".format( self.config.cfg_path, 'home/ubuntu/.cloud-install')) f.write("/var/cache/lxc var/cache/lxc none bind,create=dir\n") # Detect additional charm plugins and make available to the # container. charm_plugin_dir = self.config.getopt('charm_plugin_dir') if charm_plugin_dir \ and self.config.cfg_path not in charm_plugin_dir: plug_dir = os.path.abspath( self.config.getopt('charm_plugin_dir')) plug_base = os.path.basename(plug_dir) f.write("{d} home/ubuntu/{m} " "none bind,create=dir\n".format(d=plug_dir, m=plug_base)) extra_mounts = os.getenv("EXTRA_BIND_DIRS", None) if extra_mounts: for d in extra_mounts.split(','): mountpoint = os.path.basename(d) f.write("{d} home/ubuntu/{m} " "none bind,create=dir\n".format(d=d, m=mountpoint)) # update container config with open(os.path.join(self.container_abspath, 'config'), 'a') as f: f.write("lxc.mount.auto = cgroup:mixed\n" "lxc.start.auto = 1\n" "lxc.start.delay = 5\n" "lxc.mount = {}/fstab\n".format(self.container_abspath)) lxc_logfile = os.path.join(self.config.cfg_path, 'lxc.log') Container.start(self.container_name, lxc_logfile) Container.wait_checked(self.container_name, lxc_logfile) self.tasker.start_task("Initializing Container", self.read_cloud_init_output) tries = 0 while not self.cloud_init_finished(tries): time.sleep(1) tries += 1 # we do this here instead of using cloud-init, for greater # control over ordering log.debug("Container started, cloud-init done.") lxc_network = self.write_lxc_net_config() self.add_static_route(lxc_network) self.tasker.start_task("Installing Dependencies", self.read_progress_output) log.debug("Installing openstack & openstack-single directly, " "and juju-local, libvirt-bin and lxc via deps") Container.run(self.container_name, "env DEBIAN_FRONTEND=noninteractive apt-get -qy " "-o Dpkg::Options::=--force-confdef " "-o Dpkg::Options::=--force-confold " "install openstack openstack-single ", output_cb=self.set_progress_output) log.debug("done installing deps")
def do_install(self): self.display_controller.status_info_message("Building environment") if os.path.exists(self.container_abspath): raise Exception("Container exists, please uninstall or kill " "existing cloud before proceeding.") # check for deb early, will actually install it later upstream_deb = self.config.getopt('upstream_deb') if upstream_deb and not os.path.isfile(upstream_deb): raise Exception("Upstream deb '{}' " "not found.".format(upstream_deb)) utils.ssh_genkey() self.setup_apt_proxy() self.prep_userdata() utils.render_charm_config(self.config) self.prep_juju() self.set_perms() self.create_container_and_wait() # Copy over host ssh keys Container.cp(self.container_name, os.path.join(utils.install_home(), '.ssh/id_rsa*'), '.ssh/.') # Install local copy of openstack installer if provided if upstream_deb: shutil.copy(upstream_deb, self.config.cfg_path) self._install_upstream_deb() # Stop before we attempt to access container if self.config.getopt('install_only'): log.info("Done installing, stopping here per --install-only.") self.config.setopt('install_only', True) self.loop.exit(0) # Update jujus no-proxy setting if applicable if self.config.getopt('http_proxy') or \ self.config.getopt('https_proxy'): log.info("Updating juju environments for proxy support") lxc_net = self.config.getopt('lxc_network') self.config.update_environments_yaml( key='no-proxy', val='{},localhost,{}'.format(Container.ip(self.container_name), netutils.get_ip_set(lxc_net))) # start the party cloud_status_bin = ['openstack-status'] self.tasker.start_task("Bootstrapping Juju", self.read_progress_output) Container.run(self.container_name, "{0} juju --debug bootstrap".format( self.config.juju_home(use_expansion=True)), use_ssh=True, output_cb=self.set_progress_output) Container.run(self.container_name, "{0} juju status".format( self.config.juju_home(use_expansion=True)), use_ssh=True) self.tasker.stop_current_task() self.display_controller.status_info_message( "Starting cloud deployment") Container.run_status(self.container_name, " ".join(cloud_status_bin), self.config)
def read_cloud_init_output(self): try: s = Container.run(self.container_name, "tail -n 10 " "/var/log/cloud-init-output.log") return s.replace("\r", "") except Exception: return "Waiting..."