def list_pools(): """Return a list of all Ceph pools.""" try: pool_list = check_output(['ceph', 'osd', 'pool', 'ls']).decode('UTF-8') return pool_list except CalledProcessError as e: action_fail(str(e))
def remove_services(args): host = action_get(key="host") services = cinder_manage_service_list() if host not in ("unused", "",): services = [s for s in services if s.host == host] else: services = [s for s in services if s.host not in DEFAULT_SERVICES] removed_services = [] for service in services: log("Removing binary:{}, hostname:{}" .format(service.binary, service.host)) try: if CompareOpenStackReleases(os_release("cinder")) >= "liberty": cinder_manage_remove(service.binary, service.host) else: action_fail("Cannot remove service: {}".format(service.host)) except: action_set({'traceback': traceback.format_exc()}) action_fail("Cannot remove service: {}".format(service.host)) else: removed_services.append(service.host) action_set({'removed': ",".join(removed_services)})
def add_user(): """Add a swauth user to swift.""" if config('auth-type') == 'swauth': try_initialize_swauth() account = action_get('account') username = action_get('username') password = action_get('password') bind_port = config('bind-port') bind_port = determine_api_port(bind_port, singlenode_mode=True) success = True try: check_call([ "swauth-add-user", "-A", "http://localhost:{}/auth/".format(bind_port), "-K", leader_get('swauth-admin-key'), "-a", account, username, password]) except CalledProcessError as e: success = False log("Has a problem adding user: {}".format(e.output)) action_fail( "Adding user {} failed with: \"{}\"" .format(username, str(e))) if success: message = "Successfully added the user {}".format(username) action_set({ 'add-user.result': 'Success', 'add-user.message': message, })
def manual_backup(): backend = Backend() # keyfile is in vault directories_to_backup = action_get('directory-list') timestamp = "{:%Y-%m-%d-%H-%M-%S}".format(datetime.datetime.now()) try: log("Running a manual backup on {}".format(directories_to_backup), level=DEBUG) for directory in directories_to_backup: check_output( ["/snap/bin/preserve", "--configdir", os.path.join(os.path.expanduser("~"), ".config"), "--loglevel", "error", "create", "{name}-{timestamp}".format(name=directory, timestamp=timestamp), str(directory), "--backend", backend.get_backend(), "--vault" ]) except OSError as err: log("Create backup failed with error: {}".format(err.message), level=ERROR) action_fail("List backup failed with error: {}".format(err.message))
def restore_backup(): backend = Backend() backup_name = action_get("backup-name") restore_path = action_get("restore-path") if not os.path.exists(restore_path): log("Creating restore path: {}".format(restore_path)) os.mkdir(restore_path) try: log("Restoring backup {} to {}".format(backup_name, restore_path), level=DEBUG) check_output( ["/snap/bin/preserve", "--configdir", os.path.join(os.path.expanduser("~"), ".config"), "--loglevel", "error", "restore", "--backend", backend.get_backend(), "--vault", backup_name, restore_path]) except OSError as err: log("Restore backup failed with error: {}".format(err.message), level=ERROR) action_fail("Restore backup failed with error: {}".format(err.message))
def configure_interface(): """ Configure an ethernet interface """ iface_name = action_get('iface-name') cidr = action_get('cidr') # cidr is optional if cidr: try: # Add may fail, but change seems to add or update router.ip('address', 'change', cidr, 'dev', iface_name) except subprocess.CalledProcessError as e: action_fail('Command failed: %s (%s)' % (' '.join(e.cmd), str(e.output))) return finally: remove_state('vpe.configure-interface') status_set('active', 'ready!') try: router.ip('link', 'set', 'dev', iface_name, 'up') except subprocess.CalledProcessError as e: action_fail('Command failed: %s (%s)' % (' '.join(e.cmd), str(e.output))) finally: remove_state('vpe.configure-interface') status_set('active', 'ready!')
def create_pool(): pool_name = action_get("name") pool_type = action_get("pool-type") try: if pool_type == "replicated": replicas = action_get("replicas") replicated_pool = ReplicatedPool(name=pool_name, service='admin', replicas=replicas) replicated_pool.create() elif pool_type == "erasure": crush_profile_name = action_get("erasure-profile-name") erasure_pool = ErasurePool(name=pool_name, erasure_code_profile=crush_profile_name, service='admin') erasure_pool.create() else: log("Unknown pool type of {}. Only erasure or replicated is " "allowed".format(pool_type)) action_fail("Unknown pool type of {}. Only erasure or replicated " "is allowed".format(pool_type)) except CalledProcessError as e: action_fail("Pool creation failed because of a failed process. " "Ret Code: {} Message: {}".format(e.returncode, e.message))
def backup(): basedir = (action_get("basedir")).lower() compress = (action_get("compress")) incremental = (action_get("incremental")) sstpw = config("sst-password") optionlist = [] # innobackupex will not create recursive dirs that do not already exist, # so help it along if not os.path.exists(basedir): os.makedirs(basedir) # Build a list of options to pass to innobackupex if compress is "true": optionlist.append("--compress") if incremental is "true": optionlist.append("--incremental") try: subprocess.check_call( ['innobackupex', '--compact', '--galera-info', '--rsync', basedir, '--user=sstuser', '--password='******'time-completed': (strftime("%Y-%m-%d %H:%M:%S", gmtime())), 'outcome': 'Success'} ) except subprocess.CalledProcessError as e: action_set({ 'time-completed': (strftime("%Y-%m-%d %H:%M:%S", gmtime())), 'output': e.output, 'return-code': e.returncode, 'traceback': traceback.format_exc()}) action_fail("innobackupex failed, you should log on to the unit" "and check the status of the database")
def list_backups(): backend = Backend() try: log("Listing backups", level=DEBUG) preserve_list = check_output( ["/snap/bin/preserve", "--configdir", os.path.join(os.path.expanduser("~"), ".config"), "--loglevel", "error", "list", "--vault", "--backend", backend.get_backend(), "--json"]) try: backup_list = json.loads(preserve_list) action_set({"message": backup_list}) except ValueError as verr: log("Unable to load preserve output as json. Error {}".format( verr), level=ERROR) action_fail( "Json loading of preserve output failed: {}".format(verr)) except OSError as err: log("List backup failed with error: {}".format(err.message), level=ERROR) action_fail("List backup failed with error: {}".format(err.message))
def remove_services(): load_config_file(os.path.join(os.path.sep, "etc", "cinder", "cinder.conf")) host = action_get(key="host") services = model_query({}, models.Service, read_deleted="no", session=get_session()) if host not in ("unused", "",): services = services.filter(models.Service.host == host) else: ands = [] for service in DEFAULT_SERVICES: ands.append(and_(models.Service.host != service)) services = services.filter(*ands) removed_services = [] ctxt = context.get_admin_context() for service in services.all(): log("Removing service:%d, hostname:%s" % (service.id, service.host)) try: if os_release("cinder") >= "liberty": cinder_manage_remove(service.binary, service.host) else: db.service_destroy(ctxt, service.id) except: action_set({'traceback': traceback.format_exc()}) action_fail("Cannot remove service: %s" % service.host) else: removed_services.append(service.host) action_set({'removed': ",".join(removed_services)})
def pool_get(): key = action_get("key") pool_name = action_get("pool_name") try: value = check_output(['ceph', 'osd', 'pool', 'get', pool_name, key]) return value except CalledProcessError as e: action_fail(e.message)
def traceroute(): try: result, err = _run('traceroute -m {} {}'.format(action_get('hops'), action_get('destination'))) except: action_fail('traceroute command failed') else: # Here you can send results back from ping, if you had time to parse it action_set({'output': result}) finally: remove_flag('actions.traceroute')
def nmap(): err = '' try: result, err = _run('nmap {}'.format(action_get('destination'))) except: action_fail('nmap command failed:' + err) else: action_set({'outout': result}) finally: remove_flag('actions.nmap')
def pingme_forreal(): try: result, err = run('ping -qc {} {}'.format(action_get('count'), action_get('destination'))) except: action_fail('ping command failed') finally: remove_flag('actions.ping') # Here you can send results back from ping, if you had time to parse it action_set({'output': result})
def copy_pool(): try: source = hookenv.action_get("source") target = hookenv.action_get("target") subprocess.check_call([ 'rados', 'cppool', source, target ]) except subprocess.CalledProcessError as e: hookenv.action_fail("Error copying pool: {}".format(str(e)))
def get_disk_stats(): try: # https://www.kernel.org/doc/Documentation/iostats.txt with open('/proc/diskstats', 'r') as diskstats: return diskstats.readlines() except IOError as err: log('Could not open /proc/diskstats. Error: {}'.format(err.message)) action_fail('replace-osd failed because /proc/diskstats could not ' 'be opened {}'.format(err.message)) return None
def get_devices(): """Parse 'devices' action parameter, returns list.""" devices = [] for path in hookenv.action_get('devices').split(' '): path = path.strip() if not os.path.isabs(path): hookenv.action_fail('{}: Not absolute path.'.format(path)) raise devices.append(path) return devices
def delete_erasure_profile(): name = action_get("name") try: remove_erasure_profile(service='admin', profile_name=name) except CalledProcessError as e: log("Remove erasure profile failed with error {}".format(e.message), level="ERROR") action_fail("Remove erasure profile failed with error: {}".format( e.message))
def generate_hmac_action(*args): """Generate an HMAC in the backend HSM""" # try and get the reactive relation instance for hsm: # We only do this because there's no @action(<name>) yet that we could # access from the reactive file. hsm = reactive.RelationBase.from_state('hsm.available') if hsm is None: hookenv.action_fail( "Can't generate an HMAC in associated HSM because HSM is not " "available.") barbican.generate_hmac(hsm)
def main(args): action_name = os.path.basename(args[0]) try: action = ACTIONS[action_name] except KeyError: return "Action %s undefined" % action_name else: try: action(args) except Exception as e: action_fail(str(e))
def _rename_volume_host(currenthost, newhost): services = cinder_manage_service_list() services = [s for s in services if s.host == currenthost] if services: try: cinder_manage_volume_update_host(currenthost, newhost) except: action_set({'traceback': traceback.format_exc()}) action_fail("Cannot update host {}".format(currenthost)) else: action_fail("Cannot update host attribute from {}, {} not found" .format(currenthost, currenthost))
def not_ready_add(): actions = [ 'vpe.add-corporation', 'vpe.connect-domains', 'vpe.delete-domain-connections', 'vpe.remove-corporation', ] if helpers.any_states(*actions): action_fail('VPE is not configured') status_set('blocked', 'vpe is not configured')
def get_health(): """ Returns the output of 'ceph health'. On error, 'unknown' is returned. """ try: value = check_output(['ceph', 'health']).decode('UTF-8') return value except CalledProcessError as e: action_fail(str(e)) return 'Getting health failed, health unknown'
def list_pools(): try: cluster = connect() pool_list = cluster.list_pools() cluster.shutdown() return pool_list except (rados.IOError, rados.ObjectNotFound, rados.NoData, rados.NoSpace, rados.PermissionError) as e: action_fail(e.message)
def generate_mkek_action(*args): """Generate an MKEK in the backend HSM""" # try and get the reactive relation instance for hsm: # We only do this because there's no @action(<name>) yet that we could # access from the reactive file. hsm = reactive.RelationBase.from_state('hsm.available') if hsm is None: hookenv.action_fail( "Can't generate an MKEK in associated HSM because HSM is not " "available.") return with charms_openstack.charm.provide_charm_instance as barbican_charm: barbican_charm.generate_mkek(hsm)
def main(args): action_name = os.path.basename(args[0]) try: action = ACTIONS[action_name] except KeyError: s = "Action {} undefined".format(action_name) action_fail(s) return s else: try: action(args) except Exception as e: action_fail("Action {} failed: {}".format(action_name, str(e)))
def status(args): """Display status of cluster resources. Includes inactive resources in results.""" cmd = ['crm', 'status', '--inactive'] try: result = subprocess.check_output(cmd).decode('utf-8') action_set({'result': result}) except subprocess.CalledProcessError as e: log("ERROR: Failed call to crm resource status. " "output: {}. return-code: {}".format(e.output, e.returncode)) log(traceback.format_exc()) action_set({'result': ''}) action_fail("failed to get cluster status")
def remove_pool(): try: pool_name = action_get("name") cluster = connect() log("Deleting pool: {}".format(pool_name)) cluster.delete_pool(str(pool_name)) # Convert from unicode cluster.shutdown() except (rados.IOError, rados.ObjectNotFound, rados.NoData, rados.NoSpace, rados.PermissionError) as e: log(e) action_fail(e)
def main(argv): action_name = _get_action_name() actions_yaml_path = _get_actions_yaml_path() parser = get_action_parser(actions_yaml_path, action_name) args = parser.parse_args(argv) try: action = ACTIONS[action_name] except KeyError: return "Action %s undefined" % action_name else: try: action(args) except Exception as e: action_fail(str(e))
def remove_pool(): try: pool_name = action_get("name") set_mon_allow_pool_delete(delete=True) subprocess.check_call([ 'ceph', 'osd', 'pool', 'delete', pool_name, pool_name, '--yes-i-really-really-mean-it', ]) except subprocess.CalledProcessError as e: log(e) action_fail("Error deleting pool: {}".format(str(e))) finally: set_mon_allow_pool_delete(delete=False)
def touch(): gitDirectory = 'openair-cn' cassandraServerIP = action_get('cassandra-server-ip') status_set('active', 'configure-cassandra: configuring Cassandra ...') err = '' try: filename = '/tmp/x0' cmd = [ 'echo "{cassandraServerIP}" >{filename}'.format( filename=filename, cassandraServerIP=cassandraServerIP) ] result, err = charms.sshproxy._run(cmd) except: action_fail('command failed:' + err) else: action_set({'outout': result}) finally: clear_flag('actions.touch')
def cleanup(args): """Cleanup an/all hacluster resource(s). Optional arg "resource=res_xyz_abc" """ resource_name = (action_get("resource")).lower() if resource_name == 'all': cmd = ['crm_resource', '-C'] else: cmd = ['crm', 'resource', 'cleanup', resource_name] try: subprocess.check_call(cmd) action_set({'result': 'success'}) except subprocess.CalledProcessError as e: log("ERROR: Failed call to crm resource cleanup for {}. " "output: {}. return-code: {}".format(resource_name, e.output, e.returncode)) log(traceback.format_exc()) action_set({'result': 'failure'}) action_fail("failed to cleanup crm resource " "'{}'".format(resource_name))
def hugepages_report(): '''Action to return current hugepage usage and kernel cmdline for static hugepage allocation. Takes no params. ''' outmap = {} try: devp = "{}/devices/system/node/node*/hugepages/*/*".format(SYSFS) outmap['hugepagestats'] = subprocess.check_output( "grep -H . {}".format(devp), shell=True).decode('UTF-8') except subprocess.CalledProcessError as e: hookenv.log(e) hookenv.action_fail("Getting hugepages report failed: {}".format( e.message)) with open(KERNELCMD, 'rb') as cmdline: try: outmap['kernelcmd'] = cmdline.read().strip() except IOError as e: hookenv.action_fail('Could not read {}: {}'.format(KERNELCMD, e)) return hookenv.action_set(outmap)
def pool_get(): """ Returns a key from a pool using 'ceph osd pool get'. The key is provided via the 'key' action parameter and the pool provided by the 'pool_name' parameter. These are used when running 'ceph osd pool get <pool_name> <key>', the result of which is returned. On failure, 'unknown' will be returned. """ key = action_get("key") pool_name = action_get("pool_name") try: value = (check_output(['ceph', 'osd', 'pool', 'get', pool_name, key]).decode('UTF-8')) return value except CalledProcessError as e: action_fail(str(e)) return 'unknown'
def zap(): if not hookenv.action_get('i-really-mean-it'): hookenv.action_fail('i-really-mean-it is a required parameter') return failed_devices = [] not_block_devices = [] devices = get_devices() for device in devices: if not is_block_device(device): not_block_devices.append(device) if (is_device_mounted(device) or is_active_bluestore_device(device) or is_mapped_luks_device(device)): failed_devices.append(device) if failed_devices or not_block_devices: message = "" if failed_devices: message = "{} devices are mounted: {}".format( len(failed_devices), ", ".join(failed_devices)) if not_block_devices: if message is not '': message += "\n\n" message += "{} devices are not block devices: {}".format( len(not_block_devices), ", ".join(not_block_devices)) hookenv.action_fail(message) return db = kv() used_devices = db.get('osd-devices', []) for device in devices: zap_disk(device) if device in used_devices: used_devices.remove(device) db.set('osd-devices', used_devices) db.flush() hookenv.action_set({ 'message': "{} disk(s) have been zapped, to use them as OSDs, run: \n" "juju run-action {} add-disk osd-devices=\"{}\"".format( len(devices), hookenv.local_unit(), " ".join(devices)) })
def backup(args): basedir = (action_get("basedir")).lower() compress = action_get("compress") incremental = action_get("incremental") sstpw = _get_password("sst-password") optionlist = [] # innobackupex will not create recursive dirs that do not already exist, # so help it along if not os.path.exists(basedir): os.makedirs(basedir) # Build a list of options to pass to innobackupex if compress: optionlist.append("--compress") if incremental: optionlist.append("--incremental") # xtrabackup 2.4 (introduced in Bionic) doesn't support compact backups if CompareHostReleases(lsb_release()['DISTRIB_CODENAME']) < 'bionic': optionlist.append("--compact") try: subprocess.check_call([ 'innobackupex', '--galera-info', '--rsync', basedir, '--user=sstuser', '--password={}'.format(sstpw) ] + optionlist) action_set({ 'time-completed': (strftime("%Y-%m-%d %H:%M:%S", gmtime())), 'outcome': 'Success' }) except subprocess.CalledProcessError as e: action_set({ 'time-completed': (strftime("%Y-%m-%d %H:%M:%S", gmtime())), 'output': e.output, 'return-code': e.returncode, 'traceback': traceback.format_exc() }) action_fail("innobackupex failed, you should log on to the unit" "and check the status of the database")
def diskusage(args): """Runs 'swift-recon -d' and returns values in GB. @raises CalledProcessError on check_output failure @raises Exception on any other failure """ try: raw_output = check_output(['swift-recon', '-d']).decode('UTF-8') recon_result = list(line.strip().split(' ') for line in raw_output.splitlines() if 'Disk' in line) for line in recon_result: if 'space' in line: line[4] = str(int(line[4]) // (1024 * 1024 * 1024)) + 'GB' line[6] = str(int(line[6]) // (1024 * 1024 * 1024)) + 'GB' result = [' '.join(x) for x in recon_result] action_set({'output': result}) except CalledProcessError as e: action_set({'output': e.output}) action_fail('Failed to run swift-recon -d') except: raise
def curl_call(action_name, path, method, headers={}, data=""): try: import requests resp = None curl_url = "http://{}:{}{}".format(rest_ip, rest_port, path) request_method = getattr(requests, method.lower()) resp = request_method(curl_url, headers=headers, data=data, verify=False) result = resp.text except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] action_fail( "command failed: {}, endpoint: {}:{}, filename: {}, line: {}". format(e, rest_ip, rest_port, fname, exc_tb.tb_lineno)) else: action_set({"stdout": result, "errors": e}) finally: remove_flag(action_name)
def action_upgrade(): state = '' status = '' try: state, status = vnf_upgrade() except Exception as e: # If any exception happens within the action execution, we'll capture # it and report it via `action_fail`. Normal errors within the # execution of your operational logic should be caught, the state # should be set to STATE_FAIL and returned with a meaningful status. action_fail('Primitive failed: {} ({})'.format(e)) else: # Send the state and a status message to the operator. action_set({'state': state, 'status': status}) # If we're in a FAIL state, call action_fail to maintain compatibility # with charms not specific to OSM. if state == charms.osm.STATE_FAIL: action_fail('Primitive failed.') finally: clear_flag('actions.upgrade')
def sshcmd(): ''' Create and Activate the network corporation ''' commands = action_get('commands').split("\\n") # multi line is accepted with \n to separate then converted because juju does not allow advanced types like list or json :( cmdsMultiLines = """ {} """.format("\n".join(commands)) status_set('maintenance', 'running cmd on fortios') try: log("trying to run cmd: %s on fortios" % cmdsMultiLines) stdout, stderr = fortios.sshcmd(cmdsMultiLines) except Exception as e: action_fail('cmd on fortios failed reason:' + repr(e)) else: log('sshcmd resp %s' % stdout) action_set({'output': stdout}) remove_state('actions.sshcmd') status_set('active', 'alive')
def set_quota(): max_files = action_get('max-files') max_bytes = action_get('max-bytes') directory = action_get('directory') if not os.path.exists(directory): action_fail("Directory must exist before setting quota") attr = "ceph.quota.{}" value = None if max_files: attr.format("max_files") value = str(max_files) elif max_bytes: attr.format("max_bytes") value = str(max_bytes) try: xattr.setxattr(directory, attr, value) except IOError as err: action_fail("Unable to set xattr on {}. Error: {}".format( directory, err))
def apply_manifest(): """ Applies a user defined manifest with kubectl """ _, apply_path = tempfile.mkstemp(suffix=".json") try: manifest = json.loads(action_get("json")) with open(apply_path, "w") as manifest_file: json.dump(manifest, manifest_file) output = _kubectl(["apply", "-f", apply_path]) action_set({ "summary": "Manifest applied.", "output": output.decode("utf-8"), }) except subprocess.CalledProcessError as e: action_fail( "kubectl failed with exit code {} and message: {}".format( e.returncode, e.output)) except json.JSONDecodeError as e: action_fail("Failed to parse JSON manifest: {}".format(str(e))) except Exception as e: action_fail("Failed to apply manifest: {}".format(str(e))) finally: os.unlink(apply_path)
def main(args): action_name = os.path.basename(args[0]) try: action = ACTIONS[action_name] except KeyError: return "Action %s undefined" % action_name else: try: action(args) except vault.VaultError as e: hookenv.action_fail(str(e)) except Exception: exc = format_exc() hookenv.log(exc, hookenv.ERROR) hookenv.action_fail(exc.splitlines()[-1]) else: # we were successful, so commit changes from the action unitdata.kv().flush() # try running handlers based on new state try: charms.reactive.main() except Exception: exc = format_exc() hookenv.log(exc, hookenv.ERROR) hookenv.action_fail(exc.splitlines()[-1])
def start_transcoder(): stream_ip = action_get('stream-ip') output_port = action_get('output-port') err = '' try: cmd = "sudo rm /etc/systemd/system/opencv.service >/dev/null 2>&1; " cmd += "sudo systemctl stop opencv.service >/dev/null 2>&1; " cmd += "sudo systemctl daemon-reload" result, err = charms.sshproxy._run(cmd) except: action_fail('command failed: ' + err) remove_state('actions.start-transcoder') return err = '' try: cmd = "echo '[Unit]' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'Description=OpenCV' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo '' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo '[Service]' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'Type=simple' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'User=ubuntu' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'WorkingDirectory=/home/ubuntu' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'ExecStart=/usr/bin/python live_server.py {0} {1}' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && ".format( stream_ip, output_port) cmd += "echo 'Restart=always' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'RestartSec=5' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo '' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo '[Install]' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "echo 'WantedBy=multi-user.target' | sudo tee -a /etc/systemd/system/opencv.service > /dev/null && " cmd += "sudo systemctl daemon-reload && " cmd += "sudo systemctl start opencv.service" result, err = charms.sshproxy._run(cmd) except: action_fail('command failed: ' + err) else: action_set({'output': result, 'errors': err}) finally: remove_state('actions.start-transcoder')
def main(argv): action = os.path.basename(argv[0]) try: params = hookenv.action_get() if action == "maintenance-mode-start": maintenance_mode_start(params) elif action == "maintenance-mode-stop": maintenance_mode_stop(params) elif hookenv.leader_get("maintenance_mode") is not None: hookenv.action_fail("Application is in maintenance mode") return if action == "replication-pause": replication_pause(params) elif action == "replication-resume": replication_resume(params) elif action == "wal-e-backup": wal_e_backup(params) elif action == "wal-e-list-backups": wal_e_list_backups(params) elif action == "wal-e-restore": reactive_action("action.wal-e-restore") elif action == "switchover": reactive_action("action.switchover") elif action in ("maintenance-mode-start", "maintenance-mode-stop"): reactive_action("action.{}".format(action)) # Sets status, exits. else: hookenv.action_fail("Action {} not implemented".format(action)) except Exception: hookenv.action_fail("Unhandled exception") tb = traceback.format_exc() hookenv.action_set(dict(traceback=tb)) hookenv.log("Unhandled exception in action {}".format(action)) print(tb)
def make_cache_tier(): backer_pool = action_get("backer-pool") cache_pool = action_get("cache-pool") cache_mode = action_get("cache-mode") # Pre flight checks if not pool_exists('admin', backer_pool): log("Please create {} pool before calling create-cache-tier".format( backer_pool)) action_fail("create-cache-tier failed. Backer pool {} must exist " "before calling this".format(backer_pool)) if not pool_exists('admin', cache_pool): log("Please create {} pool before calling create-cache-tier".format( cache_pool)) action_fail("create-cache-tier failed. Cache pool {} must exist " "before calling this".format(cache_pool)) pool = Pool(service='admin', name=backer_pool) try: pool.add_cache_tier(cache_pool=cache_pool, mode=cache_mode) except CalledProcessError as err: log("Add cache tier failed with message: {}".format(err.message)) action_fail("create-cache-tier failed. Add cache tier failed with " "message: {}".format(err.message))
def check_queues(args): """Check for queues with greater than N messages. Return those queues to the user.""" queue_depth = (action_get('queue-depth')) vhost = (action_get('vhost')) # rabbitmqctl's output contains lines we don't want, such as # 'Listing queues ..' and '...done.', which may vary by release. # Actual queue results *should* always look like 'test\t0' queue_pattern = re.compile(r"^(.*)\t([0-9]+$)") try: queue_lines = check_output( ['rabbitmqctl', 'list_queues', '-q', '-p', vhost]).decode('utf-8').splitlines() filtered = filter( None, # filter out empty records map(lambda line: queue_pattern.findall(line), queue_lines)) queues = [(queue, int(size)) for [[queue, size]] in filtered] result = {queue: size for queue, size in queues if size >= queue_depth} action_set({'output': result, 'outcome': 'Success'}) except CalledProcessError as e: action_set({'output': e.output}) action_fail('Failed to run rabbitmqctl list_queues')
def set_server(): try: # Get the target service info target_ip = action_get('server-ip') target_port = action_get('server-port') data = '{{"ip" : "{}", "port" : {} }}'. \ format(target_ip, target_port) cmd = format_curl( 'POST', '/server', data, ) result, err = charms.sshproxy._run(cmd) except Exception as e: action_fail('command failed: {}, errors: {}'.format(e, e.output)) else: action_set({'stdout': result, 'errors': err}) finally: remove_flag('actions.set-server')
def action_set_policy(): """Set the policy for a given user. Sets the policy (bw and qos) for the specified user_id """ err = '' updated = False try: user_id = action_get('user_id') bw = action_get('bw') qos = action_get('qos') # If this were a functional vnf, you would perform your operation here # and may return a value to indicate success or failure. updated = True except Exception as err: action_fail(str(err)) else: action_set({'updated': updated}) finally: clear_flag('actions.set-policy')
def touch(): err = '' try: filename = action_get('filename') cmd = ['touch {}'.format(filename)] result, err = charms.sshproxy._run(cmd) cmd = ['sudo sysctl -w net.ipv4.ip_forward=1'] result, err = charms.sshproxy._run(cmd) cmd = ['sudo sysctl -w net.ipv4.conf.all.proxy_arp=1'] result, err = charms.sshproxy._run(cmd) cmd = ['sudo ip link set ens4 up'] result, err = charms.sshproxy._run(cmd) cmd = ['sudo ip link set ens5 up'] result, err = charms.sshproxy._run(cmd) cmd = ['sudo netplan apply'] result, err = charms.sshproxy._run(cmd) except: action_fail('command failed:' + err) else: action_set({'outout': result}) finally: clear_flag('actions.touch')
def upload_signed_csr(*args): if not hookenv.is_leader(): hookenv.action_fail('Please run action on lead unit') return action_config = hookenv.action_get() root_ca = action_config.get('root-ca') if root_ca: hookenv.leader_set( {'root-ca': base64.b64decode(root_ca).decode("utf-8")}) vault_pki.upload_signed_csr( base64.b64decode(action_config['pem']).decode("utf-8"), allowed_domains=action_config.get('allowed-domains'), allow_subdomains=action_config.get('allow-subdomains'), enforce_hostnames=action_config.get('enforce-hostnames'), allow_any_name=action_config.get('allow-any-name'), max_ttl=action_config.get('max-ttl')) set_flag('charm.vault.ca.ready') set_flag('pki.backend.tuned') # reissue any certificates we might previously have provided set_flag('certificates.reissue.requested') set_flag('certificates.reissue.global.requested')
def generate_root_ca(*args): if not hookenv.is_leader(): hookenv.action_fail('Please run action on lead unit') return action_config = hookenv.action_get() root_ca = vault_pki.generate_root_ca( ttl=action_config['ttl'], allow_any_name=action_config['allow-any-name'], allowed_domains=action_config['allowed-domains'], allow_bare_domains=action_config['allow-bare-domains'], allow_subdomains=action_config['allow-subdomains'], allow_glob_domains=action_config['allow-glob-domains'], enforce_hostnames=action_config['enforce-hostnames'], max_ttl=action_config['max-ttl']) hookenv.leader_set({'root-ca': root_ca}) hookenv.action_set({'output': root_ca}) set_flag('charm.vault.ca.ready') set_flag('pki.backend.tuned') # reissue any certificates we might previously have provided set_flag('certificates.reissue.requested') set_flag('certificates.reissue.global.requested')
def restart(args): """Restart services. :param args: Unused :type args: List[str] """ deferred_only = action_get("deferred-only") services = action_get("services").split() # Check input if deferred_only and services: action_fail("Cannot set deferred-only and services") return if not (deferred_only or services): action_fail("Please specify deferred-only or services") return if action_get('run-hooks'): _run_deferred_hooks() if deferred_only: os_utils.restart_services_action(deferred_only=True) else: os_utils.restart_services_action(services=services) assess_status(register_configs())
def conf_port(): """ Configure an ethernet interface """ port = action_get('port') ip = action_get('ip') mtu = action_get('mtu') netmask = action_get('netmask') if mtu: params = { "name": port, "mode": "static", "ip": ip + " " + netmask, "allowaccess": "ping", "mtu-override": "enable", "mtu": mtu } else: params = { "name": port, "mode": "static", "ip": ip + " " + netmask, "allowaccess": "ping", "vdom": "root" } status_set('maintenance', 'running cmd on fortios') try: is_set, resp = fortios.set("system", "interface", data=params) except Exception as e: action_fail('API call on fortios failed reason:' + repr(e)) else: if is_set is True: log('API call successfull response %s' % resp) action_set({'output': resp}) else: action_fail('API call on fortios failed reason:' + resp) remove_state('actions.conf-port') status_set('active', 'alive')
def user_create(): user = action_get("name") groups = action_get("groups") or "" protect_resources(user) users = user_list() if user in list(users): action_fail('User "{}" already exists.'.format(user)) return # Validate the name if re.search("[^0-9A-Za-z:@.-]+", user): msg = "User name may only contain alphanumeric characters, ':', '@', '-' or '.'" action_fail(msg) return # Create the secret # TODO: make the token format less magical so it doesn't get out of # sync with the function that creates secrets in kubernetes_control_plane.py. token = "{}::{}".format(user, layer.kubernetes_control_plane.token_generator()) if not layer.kubernetes_control_plane.create_secret( token, user, user, groups): action_fail("Failed to create secret for: {}".format(user)) return # Create a kubeconfig ca_crt = layer.kubernetes_common.ca_crt_path kubeconfig_path = "/home/ubuntu/{}-kubeconfig".format(user) endpoints = layer.kubernetes_control_plane.get_external_api_endpoints() if not endpoints: action_fail("Kubernetes client endpoints currently unavailable.") return public_server = layer.kubernetes_control_plane.get_api_urls(endpoints)[0] layer.kubernetes_common.create_kubeconfig(kubeconfig_path, public_server, ca_crt, token=token, user=user) os.chmod(kubeconfig_path, 0o644) # Tell the people what they've won fetch_cmd = "juju scp {}:{} .".format(hookenv.local_unit(), kubeconfig_path) action_set({"msg": 'User "{}" created.'.format(user)}) action_set({"users": ", ".join(list(users) + [user])}) action_set({"kubeconfig": fetch_cmd})
def action_add_user(): """Add a user to the database.""" err = '' output = '' try: username = action_get('username') bw = action_get('bw') qos = action_get('qos') tariff = action_get('tariff') # Get the configuration, which should contain the juju username and # password. The endpoint and model will be discovered automatically cfg = config() client = charms.osm.ns.NetworkService( user=cfg['juju-username'], secret=cfg['juju-password'], ) user_id = add_user(client, username, tariff) if user_id > 0: success = set_policy(client, user_id, bw, qos) else: log("user_id is 0; add_user failed.") log("Output from charm: {}".format(output)) except Exception as err: log(str(err)) log(str(traceback.format_exc())) action_fail(str(err)) else: action_set({ 'user-id': user_id, 'policy-set': success, }) finally: clear_flag('actions.add-user')
def restart_services(args): """Restart services. :param args: Unused :type args: List[str] """ deferred_only = hookenv.action_get("deferred-only") services = hookenv.action_get("services").split() # Check input if deferred_only and services: hookenv.action_fail("Cannot set deferred-only and services") return if not (deferred_only or services): hookenv.action_fail("Please specify deferred-only or services") return if hookenv.action_get('run-hooks'): _run_deferred_hooks() if deferred_only: os_utils.restart_services_action(deferred_only=True) else: os_utils.restart_services_action(services=services) with charms_openstack.charm.provide_charm_instance() as charm_instance: charm_instance._assess_status()
def get_sp_metadata(*args): if not os.path.exists(SP_METADATA_FILE): return hookenv.action_fail( "The SP metadata file {} does not exist".format(SP_METADATA_FILE)) sp_metadata = "" # By stripping double new lines and tabs we get human readable xml # Otherwise, show-action-status is a garbled mess with open(SP_METADATA_FILE, 'rt') as f: for line in f.readlines(): line = line.replace("\t", " ") if line.strip(" ") == "\n": continue sp_metadata += line return hookenv.action_set({"output": sp_metadata})
def action_run_command(): """Execute the run command via action.""" try: if in_action_context(): cmd = action_get('command') output, err = run_command(cmd) if len(err): action_fail("Command '{}' returned error code {}".format( cmd, err)) else: if isinstance(output, bytes): action_set({'output': output[:100000]}) elif isinstance(output, str): action_set( {'output': textwrap.shorten(output, width=100000)}) else: action_set({'output': output}) except subprocess.CalledProcessError as e: if in_action_context(): action_fail('Command failed: %s (%s)' % (' '.join(e.cmd), str(e.output))) finally: clear_flag('actions.run')
def remove_services(): load_config_file(os.path.join(os.path.sep, "etc", "cinder", "cinder.conf")) host = action_get(key="host") services = model_query({}, models.Service, read_deleted="no", session=get_session()) if host not in ( "unused", "", ): services = services.filter(models.Service.host == host) else: ands = [] for service in DEFAULT_SERVICES: ands.append(and_(models.Service.host != service)) services = services.filter(*ands) removed_services = [] ctxt = context.get_admin_context() for service in services.all(): log("Removing service:%d, hostname:%s" % (service.id, service.host)) try: if os_release("cinder") >= "liberty": cinder_manage_remove(service.binary, service.host) else: db.service_destroy(ctxt, service.id) except: action_set({'traceback': traceback.format_exc()}) action_fail("Cannot remove service: %s" % service.host) else: removed_services.append(service.host) action_set({'removed': ",".join(removed_services)})