def _install_upstream_deb(self): log.info('Found upstream deb, installing that instead') filename = os.path.basename(self.config.getopt('upstream_deb')) try: utils.container_run( self.container_name, 'sudo dpkg -i /home/ubuntu/.cloud-install/{}'.format(filename)) except: # Make sure deps are installed if any new ones introduced by # the upstream packaging. utils.container_run(self.container_name, 'sudo apt-get install -qyf')
def _install_upstream_deb(self): log.info('Found upstream deb, installing that instead') filename = os.path.basename(self.config.getopt('upstream_deb')) try: utils.container_run( self.container_name, 'sudo dpkg -i /home/ubuntu/.cloud-install/{}'.format( filename)) except: # Make sure deps are installed if any new ones introduced by # the upstream packaging. utils.container_run( self.container_name, 'sudo apt-get install -qyf')
def cloud_init_finished(self): """checks cloud-init result.json in container to find out status 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 = utils.container_run(self.container_name, cmd) # convert from bytes for json to process result_json = result_json.decode("utf-8") log.debug(result_json) except: log.debug("Waiting for cloud-init status result") return False if result_json == '': return False ret = json.loads(result_json) errors = ret['v1']['errors'] if len(errors): log.error("Container cloud-init finished with " "errors: {}".format(errors)) # FIXME: Log errors for now, don't be fatal as the main # error is coming from a pollinate command unable # to run which doesn't seem to effect the installer. # raise Exception("Container cloud-init returned errors") return True
def read_juju_log(self): try: return utils.container_run(self.container_name, 'tail -n 10 ' '/var/log/juju-ubuntu-local' '/all-machines.log') except Exception: return "Waiting..."
def read_cloud_init_output(self): try: s = utils.container_run(self.container_name, 'tail -n 10 ' '/var/log/cloud-init-output.log') return s.replace('\r', '') 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 = utils.container_run(self.container_name, cmd) except utils.NoContainerIPException as e: log.debug("Container has no IPs according to lxc-info. " "Will retry.") return False except utils.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 do_install(self): self.display_controller.info_message("Building environment") if os.path.exists(self.container_abspath): # Container exists, handle return code in installer raise Exception("Container exists, please uninstall or kill " "existing cloud before proceeding.") utils.ssh_genkey() # Preparations self.prep_userdata() # setup charm configurations utils.render_charm_config(self.config, self.opts) self.prep_juju() # Set permissions self.set_perms() # Start container self.create_container_and_wait() # Install local copy of openstack installer if provided if self.opts.upstream_deb and os.path.isfile(self.opts.upstream_deb): shutil.copy(self.opts.upstream_deb, self.config.cfg_path) self._install_upstream_deb() # start the party cloud_status_bin = ['openstack-status'] self.display_controller.info_message("Bootstrapping Juju") self.start_task("Bootstrapping Juju") utils.container_run( self.container_name, "JUJU_HOME={} juju bootstrap".format( self.config.cfg_path)) utils.container_run( self.container_name, "JUJU_HOME={} juju status".format( self.config.cfg_path)) if self.opts.install_only: log.info("Done installing, stopping here per --install-only.") sys.exit(0) self.display_controller.info_message("Starting cloud deployment") utils.container_run_status( self.container_name, " ".join(cloud_status_bin))
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 = utils.container_run(self.container_name, cmd) except utils.NoContainerIPException as e: log.debug("Container has no IPs according to lxc-info. " "Will retry.") return False except utils.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 ret = json.loads(result_json) 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 do_install(self): self.display_controller.info_message("Building environment") if os.path.exists(self.container_abspath): # Container exists, handle return code in installer raise Exception("Container exists, please uninstall or kill " "existing cloud before proceeding.") utils.ssh_genkey() # Prepare cloud-init file for creation self.prep_userdata() # Start container self.create_container_and_wait() # configure juju environment for bootstrap single_env = utils.load_template('juju-env/single.yaml') single_env_modified = single_env.render( openstack_password=self.config.openstack_password) utils.spew('/tmp/single.yaml', single_env_modified) utils.container_run(self.container_name, 'mkdir -p .juju') utils.container_cp(self.container_name, '/tmp/single.yaml', '.juju/environments.yaml') # Set permissions self.copy_installdata_and_set_perms() # start the party cloud_status_bin = ['openstack-status'] if self.opts.enable_swift: cloud_status_bin.append('--enable-swift') self.display_controller.info_message("Bootstrapping Juju ..") self.start_task("Starting Juju server") utils.container_run(self.container_name, "juju bootstrap") utils.container_run(self.container_name, "juju status") if self.opts.install_only: log.info("Done installing, stopping here per --install-only.") sys.exit(0) self.display_controller.info_message("Starting cloud deployment ..") utils.container_run_status( self.container_name, " ".join(cloud_status_bin))
def copy_installdata_and_set_perms(self): """ copies install data and sets permissions on files/dirs """ try: utils.chown(self.config.cfg_path, utils.install_user(), utils.install_user(), recursive=True) except: raise SingleInstallException( "Unable to set ownership for {}".format(self.config.cfg_path)) # copy over the rest of our installation data from host # and setup permissions # setup charm configurations charm_conf = utils.load_template('charmconf.yaml') charm_conf_modified = charm_conf.render( openstack_password=self.config.openstack_password) utils.spew(os.path.join(self.config.cfg_path, 'charmconf.yaml'), charm_conf_modified) utils.container_run( self.container_name, 'mkdir -p .cloud-install') utils.container_run( self.container_name, 'sudo mkdir -p /etc/openstack') utils.container_cp(self.container_name, os.path.join( utils.install_home(), '.cloud-install/*'), '.cloud-install/.') # our ssh keys too utils.container_cp(self.container_name, os.path.join(utils.install_home(), '.ssh/id_rsa*'), '.ssh/.') utils.container_run(self.container_name, "chmod 600 .ssh/id_rsa*")
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.prep_userdata() utils.render_charm_config(self.config) self.prep_juju() self.set_perms() self.create_container_and_wait() # Copy over host ssh keys utils.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( utils.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") utils.container_run(self.container_name, "{0} juju bootstrap".format( self.config.juju_home(use_expansion=True)), use_ssh=True) utils.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") utils.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") utils.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') utils.container_start(self.container_name, lxc_logfile) utils.container_wait_checked(self.container_name, lxc_logfile) 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") log.debug("Installing openstack & openstack-single directly, " "and juju-local, libvirt-bin and lxc via deps") utils.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")
def _install_upstream_deb(self): log.debug('Found upstream deb, installing that instead') filename = os.path.basename(self.opts.upstream_deb) utils.container_run( self.container_name, 'sudo dpkg -i .cloud-install/{}'.format( filename))
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.prep_userdata() utils.render_charm_config(self.config) self.prep_juju() self.set_perms() self.create_container_and_wait() # Copy over host ssh keys utils.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( utils.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") utils.container_run(self.container_name, "{0} juju bootstrap".format( self.config.juju_home(use_expansion=True)), use_ssh=True) utils.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") utils.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") utils.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') utils.container_start(self.container_name, lxc_logfile) utils.container_wait_checked(self.container_name, lxc_logfile) 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") log.debug("Installing openstack & openstack-single directly, " "and juju-local, libvirt-bin and lxc via deps") utils.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")