def hook_install(self): cfg = self.config self.hook_uninstall() self.generate_locales((u'fr_CH.UTF-8',)) try_makedirs(u'/etc/mysql') debconf, mysql = u'debconf-set-selections', u'mysql-server mysql-server' # Tip : http://ubuntuforums.org/showthread.php?t=981801 self.cmd(debconf, input=u'{0}/root_password select {1}'.format(mysql, cfg.mysql_root_password)) self.cmd(debconf, input=u'{0}/root_password_again select {1}'.format(mysql, cfg.mysql_root_password)) self.install_packages(WebuiHooks.PACKAGES) self.restart_ntp() self.info(u'Import Web UI database and create user') hostname = socket.gethostname() self.cmd(u'service mysql start', fail=False) self.mysql_do(u"DROP USER ''@'localhost'; DROP USER ''@'{0}';".format(hostname), fail=False) self.mysql_do(u"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%%' WITH GRANT OPTION;") self.mysql_do(u'DROP DATABASE IF EXISTS webui') self.mysql_do(cli_input=open(self.local_config.site_database_file, u'r', u'utf-8').read()) self.mysql_do(u"GRANT ALL ON webui.* TO 'webui'@'%%' IDENTIFIED BY '{0}';".format(cfg.mysql_user_password)) self.info(u'Configure Apache 2') self.cmd(u'a2enmod rewrite') self.info(u'Copy and pre-configure Web UI') rsync(u'www/', self.local_config.site_directory, archive=True, delete=True, exclude_vcs=True, recursive=True) chown(self.local_config.site_directory, DAEMON_USER, DAEMON_GROUP, recursive=True) self.local_config.encryption_key = WebuiHooks.randpass(32) self.info(u'Expose Apache 2 service') self.open_port(80, u'TCP')
def post_install(): from encodebox import lib from pytoolbox.console import confirm from pytoolbox.encoding import to_bytes from pytoolbox.filesystem import chown, from_template, try_makedirs, try_remove from pytoolbox.network.http import download if not exists(u'/usr/local/bin/neroAacEnc'): try: print(u'Download and install Nero AAC encoder') download(u'ftp://ftp6.nero.com/tools/NeroDigitalAudio.zip', u'/tmp/nero.zip') zipfile.ZipFile(u'/tmp/nero.zip').extract(u'linux/neroAacEnc', u'/usr/local/bin') os.chmod( u'/usr/local/bin/neroAacEnc', os.stat(u'/usr/local/bin/neroAacEnc').st_mode | stat.S_IEXEC) finally: try_remove(u'/tmp/nero.zip') filename = lib.SETTINGS_FILENAME settings = lib.load_settings(u'etc/config.yaml') if not exists(filename) or confirm( u'Overwrite existing configuration file "{0}"'.format(filename)): print(u'Generate configuration file "{0}"'.format(filename)) password = lib.generate_password() settings[u'rabbit_password'] = password lib.save_settings(filename, settings) print(u'Configure RabbitMQ Message Broker') check_call([u'service', u'rabbitmq-server', u'start']) call([u'rabbitmqctl', u'add_vhost', u'/']) call([u'rabbitmqctl', u'delete_user', u'guest']) call([u'rabbitmqctl', u'delete_user', u'encodebox']) call([ u'rabbitmqctl', u'add_user', u'encodebox', settings[u'rabbit_password'] ]) check_call([ u'rabbitmqctl', u'set_permissions', u'-p', u'/', u'encodebox', u'.*', u'.*', u'.*' ]) users, vhosts = lib.rabbit_users(), lib.rabbit_vhosts() print(u'RabbitMQ users: {0} vhosts: {1}'.format(users, vhosts)) if u'guest' in users or u'encodebox' not in users: raise RuntimeError(to_bytes(u'Unable to configure RabbitMQ')) print(u'Create directory for storing persistent data') try_makedirs(lib.LIB_DIRECTORY) chown(lib.LIB_DIRECTORY, lib.USERNAME, pwd.getpwnam(lib.USERNAME).pw_gid, recursive=True) print(u'Register and start our services as user ' + lib.USERNAME) from_template(u'etc/encodebox.conf.template', u'/etc/supervisor/conf.d/encodebox.conf', { u'lib_directory': lib.LIB_DIRECTORY, u'user': lib.USERNAME }) call([u'service', u'supervisor', u'force-reload'])
def storage_remount(self, address=None, fstype=None, mountpoint=None, options=u''): if self.storage_config_is_enabled: self.info(u'Override storage parameters with charm configuration') address = self.config.storage_address nat_address = self.config.storage_nat_address fstype = self.config.storage_fstype mountpoint = self.config.storage_mountpoint options = self.config.storage_options elif address and fstype and mountpoint: self.info(u'Use storage parameters from charm storage relation') nat_address = u'' else: return if nat_address: self.info(u'Update hosts file to map storage internal address {0} to {1}'.format(address, nat_address)) lines = filter(lambda l: nat_address not in l, open(self.local_config.hosts_file, u'r', u'utf-8')) lines += u'{0} {1}\n'.format(nat_address, address) open(self.local_config.hosts_file, u'w', u'utf-8').write(u''.join(lines)) # Avoid unregistering and registering storage if it does not change ... if (address == self.local_config.storage_address and nat_address == self.local_config.storage_nat_address and fstype == self.local_config.storage_fstype and mountpoint == self.local_config.storage_mountpoint and options == self.local_config.storage_options): self.remark(u'Skip remount already mounted shared storage') else: self.storage_unregister() self.debug(u"Mount shared storage [{0}] {1}:{2} type {3} options '{4}' -> {5}".format(nat_address, address, mountpoint, fstype, options, self.local_config.storage_path)) try_makedirs(self.local_config.storage_path) # FIXME try X times, a better way to handle failure for i in xrange(self.local_config.storage_mount_max_retry): if self.storage_is_mounted: break mount_address = u'{0}:/{1}'.format(nat_address or address, mountpoint) mount_path = self.local_config.storage_path if options: self.cmd([u'mount', u'-t', fstype, u'-o', options, mount_address, mount_path]) else: self.cmd([u'mount', u'-t', fstype, mount_address, mount_path]) time.sleep(self.local_config.storage_mount_sleep_delay) if self.storage_is_mounted: # FIXME update /etc/fstab (?) self.local_config.storage_address = address self.local_config.storage_nat_address = nat_address self.local_config.storage_fstype = fstype self.local_config.storage_mountpoint = mountpoint self.local_config.storage_options = options self.remark(u'Shared storage successfully registered') self.debug(u'Create directories in the shared storage and ensure it is owned by the right user') try_makedirs(self.local_config.storage_medias_path()) try_makedirs(self.local_config.storage_uploads_path) chown(self.local_config.storage_path, DAEMON_USER, DAEMON_GROUP, recursive=True) else: raise IOError(to_bytes(u'Unable to mount shared storage'))
def hook_uninstall(self): self.info(u'Uninstall prerequisities, unregister service and load default configuration') self.hook_stop() self.storage_unregister() self.subordinate_unregister() if self.config.cleanup: self.cmd(u'apt-get -y remove --purge {0}'.format(u' '.join(PublisherHooks.PACKAGES))) self.cmd(u'apt-get -y remove --purge {0}'.format(u' '.join(PublisherHooks.FIX_PACKAGES)), fail=False) self.cmd(u'apt-get -y autoremove') shutil.rmtree(u'/etc/apache2/', ignore_errors=True) shutil.rmtree(u'/var/log/apache2/', ignore_errors=True) shutil.rmtree(self.publish_path, ignore_errors=True) os.makedirs(self.publish_path) chown(self.publish_path, DAEMON_USER, DAEMON_GROUP, recursive=True) self.local_config.reset() self.local_config.update_publish_uri(self.public_address)
def hook_install(self): local_cfg = self.local_config self.hook_uninstall() self.generate_locales((u'fr_CH.UTF-8',)) self.install_packages(OrchestraHooks.PACKAGES + OrchestraHooks.JUJU_PACKAGES, ppas=OrchestraHooks.PPAS) self.restart_ntp() self.info(u'Copy Orchestra and the local charms repository of OSCIED') rsync(local_cfg.api_path, local_cfg.site_directory, **self.rsync_kwargs) chown(local_cfg.site_directory, DAEMON_USER, DAEMON_GROUP, recursive=True) self.info(u'Expose RESTful API, MongoDB & RabbitMQ service') self.open_port(80, u'TCP') # Orchestra RESTful API self.open_port(27017, u'TCP') # MongoDB port mongod and mongos instances #self.open_port(27018, u'TCP') # MongoDB port when running with shardsvr setting #self.open_port(27019, u'TCP') # MongoDB port when running with configsvr setting #self.open_port(28017, u'TCP') # MongoDB port for the web status page. This is always +1000 self.open_port(5672, u'TCP') # RabbitMQ service
def start_celery_worker(self, retries=5, delay=2): worker_name = self.local_config.worker_name self.info(u'Start the {0} worker'.format(worker_name)) self.info(u"Ensure that the worker's directory is owned by the right user") chown(self.directory, DAEMON_USER, DAEMON_GROUP, recursive=True) start_time = time.time() for start_delay in xrange(retries): if self.cmd(u'service {0} status'.format(worker_name), fail=False)[u'returncode'] == 0: delta_time = time.time() - start_time self.remark(u'Worker {0} successfully started in {1:0.1f} seconds'.format(worker_name, delta_time)) return else: self.cmd(u'service {0} start'.format(worker_name)) time.sleep(delay) # FIXME maybe the latest attempt was successful and we do not check ... hum raise RuntimeError(to_bytes(u'Worker {0} is not ready'.format(worker_name)))
def test_chown(tmp_path): file_a = tmp_path / 'a.txt' file_b = tmp_path / 'b.txt' file_c = tmp_path / 'other' / 'c.txt' filesystem.makedirs(file_c, parent=True) Path(file_a).touch() Path(file_b).touch() Path(file_c).touch() with mock.patch('os.chown') as chown: filesystem.chown(file_a, 'root') chown.assert_called_once_with(file_a, 0, -1) with mock.patch('os.chown') as chown: filesystem.chown(tmp_path, 100, 'root') chown.assert_called_once_with(tmp_path, 100, 0) with mock.patch('os.chown') as chown: filesystem.chown(tmp_path, 100, 'root', recursive=True) chown.assert_has_calls([ mock.call(str(tmp_path), 100, 0), mock.call(str(file_b), 100, 0), mock.call(str(file_a), 100, 0), mock.call(str(file_c.parent), 100, 0), mock.call(str(file_c), 100, 0), ], any_order=True)
def save_local_config(self): u"""Save or update local configuration in charm's and api's path and ensure that is owned by the right user.""" super(OrchestraHooks, self).save_local_config() self.local_config.write(self.local_config.site_local_config_file, makedirs=True) chown(self.local_config.site_local_config_file, DAEMON_USER, DAEMON_GROUP)
def hook_config_changed(self): cfg, local_cfg = self.config, self.local_config # Apache site files must end with .conf for a2ensite to work site_file = self.name_slug + ".conf" self.info(u'Start MongoDB and RabbitMQ daemons') self.cmd(u'service mongodb start', fail=False) self.cmd(u'service rabbitmq-server start', fail=False) self.info(u'Configure JuJu Service Orchestrator') juju_config_path = dirname(local_cfg.juju_config_file) rsync(local_cfg.juju_template_path, juju_config_path, **self.rsync_kwargs) chown(juju_config_path, DAEMON_USER, DAEMON_GROUP, recursive=True) self.info(u'Configure Secure Shell') rsync(local_cfg.ssh_template_path, local_cfg.ssh_config_path, **self.rsync_kwargs) chown(local_cfg.ssh_config_path, DAEMON_USER, DAEMON_GROUP, recursive=True) self.info(u'Configure Apache 2') self.template2config(local_cfg.htaccess_template_file, local_cfg.htaccess_config_file, {}) self.template2config(local_cfg.site_template_file, join(local_cfg.sites_available_path, site_file), { u'alias': self.api_alias, u'directory': local_cfg.site_directory, u'domain': self.public_address, u'wsgi': local_cfg.api_wsgi }) self.cmd(u'a2dissite 000-default') self.cmd(u'a2ensite {0}'.format(site_file)) self.info(u'Configure MongoDB Scalable NoSQL DB') with open(u'f.js', u'w', u'utf-8') as mongo_f: mongo_f.write(u"db.addUser('admin', '{0}', false);".format(cfg.mongo_admin_password)) with open(u'g.js', u'w', u'utf-8') as mongo_g: mongo_g.write(u"db.addUser('node', '{0}', false);".format(cfg.mongo_node_password)) self.cmd(u'mongo f.js') self.cmd(u'mongo orchestra f.js') self.cmd(u'mongo celery g.js') [os.remove(f) for f in (u'f.js', u'g.js')] mongo_config = ConfigObj(local_cfg.mongo_config_file) mongo_config[u'bind_ip'] = u'0.0.0.0' mongo_config[u'noauth'] = u'false' mongo_config[u'auth'] = u'true' mongo_config.write() self.configure_rabbitmq() if cfg.plugit_api_url: self.info(u'Configure PlugIt server') infos = { u'api_url': cfg.plugit_api_url, u'debug': cfg.verbose, u'base_url': cfg.plugit_base_url, u'allowed_networks': u'", "'.join(cfg.plugit_allowed_networks.split(u',')) } self.template2config(local_cfg.plugit_template_file, local_cfg.plugit_config_file, infos) self.info(u'Configure Orchestra the Orchestrator') local_cfg.verbose = cfg.verbose local_cfg.api_url = self.api_url(local=False) local_cfg.charms_release = cfg.charms_release local_cfg.node_secret = cfg.node_secret local_cfg.root_secret = cfg.root_secret local_cfg.mongo_admin_connection = self.mongo_admin_connection local_cfg.mongo_node_connection = self.mongo_node_connection local_cfg.rabbit_connection = self.rabbit_connection infos = { u'rabbit': unicode(self.rabbit_connection), u'port': unicode(27017), u'username': u'node', u'password': unicode(cfg.mongo_node_password), } self.template2config(local_cfg.celery_template_file, local_cfg.celery_config_file, infos) local_cfg.email_server = cfg.email_server local_cfg.email_tls = cfg.email_tls local_cfg.email_address = cfg.email_address local_cfg.email_username = cfg.email_username local_cfg.email_password = cfg.email_password local_cfg.plugit_api_url = cfg.plugit_api_url self.remark(u'Orchestrator successfully configured') self.info(u'Symlink charms default directory to directory for release {0}'.format(cfg.charms_release)) try_symlink(abspath(local_cfg.charms_default_path), abspath(local_cfg.charms_release_path)) self.info(u'Ensure that the Apache sites directory is owned by the right user') chown(local_cfg.sites_directory, DAEMON_USER, DAEMON_GROUP, recursive=True) self.info(u'Configure Cronjob') self.template2config(local_cfg.cronjob_template_file, '/etc/cron.d/cron_enco', {}) self.storage_remount()