def _run_prep_vol_to_cluster_script(fs): # TODO: move to run # TODO: handle the cases where fs.get_winid is None from smb import PROJECTROOT from os import path vol_to_cluster_script = path.realpath( path.join(PROJECTROOT, 'src', 'smb', 'cli', 'powershell', 'prep_vol_to_cluster.ps1')) if fs.get_winid() is None: log_n_raise(logger, "Can't prepare volume {}".format(fs.get_name()), level=ERROR, color="red") try: cmd = execute_assert_success([ 'powershell', '.', '"' + vol_to_cluster_script.replace('\\', '/') + '"' + " -DiskNumber {} -MountPath {}".format( fs.get_winid(), fs.get_mountpoint()) ]) except: error = sys.exc_info()[1] log_n_raise(logger, "{} failed with error: {}".format(vol_to_cluster_script, error), disable_print=True)
def exit_if_vol_not_mapped(volume): ''' receives an infinisdk volume type and checks if mapped''' def _is_vol_mapped(volume_serial, timeout=3): from time import sleep for n in range(0, timeout): execute_assert_success(['powershell', '-c', 'Update-HostStorageCache']) try: execute_assert_success(['powershell', '-c', 'Get-Disk', '-SerialNumber', str(volume_serial)]) return True except: sleep(1) continue return False def _rescan(): ''' From what I saw on 90% of the times the volume just apear on both nodes If it doesn't we'll rescan ''' HPT_BIN_FILE = 'infinihost.exe' # to do need to think if we'd like to scan on remote and verify hpt_bin = path.realpath(path.join(PROJECTROOT, pardir, 'Host Power Tools', 'bin', HPT_BIN_FILE)) execute([hpt_bin, 'rescan']) if not _is_vol_mapped(volume.get_serial()): _rescan() if not _is_vol_mapped(volume.get_serial()): log_n_raise(logger, "Windows couldn't gain access to volume {} which was just mapped".format(volume.get_name()))
def _validate_max_amount_of_volumes(sdk): from smb.cli.__version__ import __version__ cluster = sdk.get_cluster() if len(cluster.get_luns()) >= MAX_ATTACHED_VOLUMES: message = "Version: {} Supports only up to {} simultaneously attached Volumes" log_n_raise(logger, message.format(__version__, MAX_ATTACHED_VOLUMES), level=WARNING)
def approve_operation(): if sys.version_info > (3, 0): _input = input else: _input = raw_input proceed = _input("Choose yes or no [y/N] ").lower() in ('y', 'yes') if not proceed: log_n_raise(logger, "user didn't confirm operation")
def am_I_master(): from platform import node config = config_get(silent=True) cmd = execute_assert_success(['powershell', '-c', 'Get-ClusterGroup', '-name', config['FSRoleName'], '|', 'Select-Object', '-ExpandProperty', 'OwnerNode', '|', 'Select-Object', '-ExpandProperty', 'name']) if cmd.get_stdout().strip() == node(): return True else: log_n_raise(logger, "The Node you are running on is NOT the Active Cluster Node")
def _validate_vol(ibox_sdk, volume_name): from infinisdk.core.type_binder import ObjectNotFound try: return ibox_sdk.volumes.choose(name=volume_name) except ObjectNotFound: log_n_raise( logger, "Volume {} couldn't be found on {}".format(volume_name, ibox_sdk.get_name()))
def _validate_pool_name(pool_name, ibox_sdk): from infinisdk.core.type_binder import ObjectNotFound try: pool = ibox_sdk.pools.choose(name=pool_name) return pool except ObjectNotFound: log_n_raise( logger, "Pool {} couldn't be found on {}".format(pool_name, ibox_sdk.get_name()))
def _validate_pool(pool_name, ibox_sdk, size): from capacity import GiB pool = _validate_pool_name(pool_name, ibox_sdk) spare_size = 1 * GiB new_free = pool.get_free_virtual_capacity() - size - spare_size if int(new_free <= 0): log_n_raise( logger, "Pool {} doesn't have enough space to provision {!r}".format( pool_name, size)) return pool
def run(cmd, error_prefix): result = execute(cmd) if result.get_returncode() == 0: return result.get_stdout() if "You do not have administrative privileges on the cluster" in result.get_stderr( ): log_n_raise(logger, "{} Cluster Permissions issue".format(error_prefix)) log_n_raise(logger, "{} {}".format(error_prefix, result.get_stderr()), disable_print=True)
def map_vol_to_cluster_infinibox(volume, sdk): cluster = sdk.get_cluster() try: mapping = cluster.map_volume(volume) except: error = sys.exc_info()[1] log_n_raise( logger, "Couldn't Map Volume {} to {}! {!r}".format( volume.get_name(), cluster.get_name(), str(error.message))) log(logger, "Mapping {} to {}".format(volume.get_name(), cluster.get_name()), level=INFO)
def _validate_vol_name(volume_name): '''Spaces in names aren't allowed''' if ' ' in volume_name: log_n_raise( logger, "Spaces aren't allowed in FS names. Please rename '{}'".format( volume_name), level=WARNING) if len(volume_name) > MAX_VOL_NAME_LENGTH: log_n_raise( logger, "'{}' FileSystem name is too long! (can be up to {} characters)". format(volume_name, MAX_VOL_NAME_LENGTH), level=WARNING)
def ibox_login(self): '''tries to connect using credintal store''' from smb.cli.smb_log import get_logger, log_n_raise logger = get_logger() store = initiate_store(self.config['IboxAddress']) ibox = infinisdk.InfiniBox(str(self.config['IboxAddress']), auth=(store.get_username(), store.get_password())) response = ibox.login() if response.status_code == 200: return ibox else: log_n_raise( logger, "Couldn't connect to InfiniBox with current credentials")
def _validate_size(size_str, roundup=False): import capacity from capacity import byte if size_str == '0' or size_str is None: return 0 try: size = capacity.from_string(size_str) except ValueError: log_n_raise(logger, "{} is an invalid capacity ! Please try one of the following:\n".format(size_str) + "<number> KB, KiB, MB, MiB, GB, GiB, TB, TiB... ", level=WARNING) if size == capacity.Capacity(0): return 0 if roundup: if (size / byte) / 512.0 != int((size / byte) / 512.0): size = ((int((size / byte) / 512) + 1) * 512) * byte return size
def change_powershell_config(key, value): import fileinput config = read_config(conf_file) if not config: log_n_raise(logger, "Problem reading config file") log(logger, "Changing {} = {}".format(key, value), level=INFO) for line in fileinput.input(conf_file, inplace=True): if "=" not in line: print line, continue line_key, line_val = line.split("=") if line_key.strip() == key: print ' {} = "{}"'.format(key, value) else: print line, fileinput.close()
def config_get(silent=False, skip_validation=False): if not path.exists(conf_dir): mkdir(conf_dir) generate_config() config = read_config(conf_file) if silent or not config: return config log(logger, "Current Config:", level=INFO, raw=True) for key, val in config.items(): message = " {}: {}".format(key, val) log(logger, message, level=INFO, raw=True) if not skip_validation: validate_config(config) if not path.exists(infinihost_bin): log_n_raise( logger, 'smb.cli depends on "Host Power Tools" and can NOT find it!') return config
def fs_detach(fsname, sdk): from smb.cli.share import get_all_shares_data, join_fs_and_share from smb.cli.share import share_delete all_filesystems = _get_all_fs(sdk) if fsname not in [fs['fsname'] for fs in all_filesystems]: log_n_raise(logger, "{} Does NOT exist. Typo?".format(fsname)) volume = _validate_vol(sdk.get_ibox(), fsname) volume_name = volume.get_name() fs = Fs(volume, sdk) shares = get_all_shares_data() full_share_list = join_fs_and_share(all_filesystems, shares) for s in full_share_list: if s.get_fs()['fsname'] == fs.get_name(): share_delete(s.get_name()) ps_cmd._run_remove_partition_access_path(fs.get_winid(), fs.get_mountpoint()) ps_cmd._run_move_cluster_volume_offline(volume_name) lib.cluster_remove_ms_volume_and_wait(volume_name) unmap_volume(volume_name, fs.get_mountpoint(), sdk)
def create_volume_on_infinibox(volume_name, pool_name, size, sdk): ibox = sdk.get_ibox() pool = _validate_pool(pool_name, ibox, size) try: log(logger, "Creating Volume {} at {}".format(volume_name, pool_name), level=INFO) volume = ibox.volumes.create(name=volume_name, pool=pool, size=size, provtype='THIN') volume.set_metadata('volume.provisionedby', 'smb.cli-{}'.format(__version__)) return volume except: error = sys.exc_info()[1] log_n_raise( logger, "Volume {} couldn't be created. {!r}".format( volume_name, str(error.message)))
def _run_attach_vol_to_cluster_script(fs): # TODO: move to run from smb import PROJECTROOT from os import path attach_vol_to_cluster_script = path.realpath( path.join(PROJECTROOT, 'src', 'smb', 'cli', 'powershell', 'add_vol_to_cluster.ps1')) try: cmd = execute_assert_success([ 'powershell', '.', '"' + attach_vol_to_cluster_script.replace('\\', '/') + '"' + " -DiskNumber {}".format(fs.get_winid()) ]) except: error = sys.exc_info()[1] log_n_raise(logger, "{} failed with error: {}".format( attach_vol_to_cluster_script, error), disable_print=True)
def validate_config(config): import re default_val = re.compile('<.+>$') keys = [ 'FSRoleName', 'PoolName', 'Cluster', 'MountRoot', 'TempDriveLetter', 'IboxAddress' ] for key in keys: if key not in config: log_n_raise( logger, "Not all parameters are in the config file. Config file {} is Invalid" .format(conf_file)) for key, val in config.iteritems(): if re.search(default_val, val): log_n_raise( logger, "Some configuration values are missing.\ne.g. {} = {}".format( key, val))
def fs_attach(volume_name, sdk, force=False): ibox = sdk.get_ibox() _validate_max_amount_of_volumes(sdk) _validate_vol_name(volume_name) volume = _validate_vol(ibox, volume_name) if force and lib.is_volume_mapped_to_cluster(volume, sdk): pass else: map_vol_to_cluster_infinibox(volume, sdk) lib.exit_if_vol_not_mapped(volume) fs = Fs(volume, sdk) _mountpoint_exist(fs.get_mountpoint()) try: ps_cmd._run_attach_vol_to_cluster_script(fs) except: log_n_raise(logger, "Couldn't add {} to SMB Cluster".format(volume_name)) log(logger, "Volume {} Attached to Cluster Successfully.".format(volume_name), level=INFO, color="green")
def validate_key_val(key, val): import re MAX_ROLE_NAME = 15 MAX_POOL_NAME = 32 key_lower = key.lower() regular_chars = re.compile('[a-zA-Z0-9\-\_]+$') dns_name = re.compile('([a-zA-Z0-9\-\_]+\.)+([a-zA-Z0-9\-\_]+$)') ip_address = re.compile( '((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9]?[0-9])' ) ms_drive_letter = re.compile(r'^[a-zA-Z]{1}:{1}\\{1}$') if key_lower in ['fsrolename', 'poolname', 'cluster']: if not re.match(regular_chars, val): log_n_raise( logger, "Only non-spacial Characters are Supported for {}".format(key)) if key_lower in ['fsrolename', 'cluster']: if len(val) > MAX_ROLE_NAME: log_n_raise( logger, "{} Value is to long. Max allowed Characters are {}".format( key, MAX_ROLE_NAME)) # add verify that the cluster and cluster role is the same if key_lower == 'poolname' and len(val) > MAX_POOL_NAME: log_n_raise( logger, "{} Value is to long. Max allowed Characters are {}".format( key, MAX_POOL_NAME)) if key_lower in ['mountroot', 'tempdriveletter']: if not re.match(ms_drive_letter, val): log_n_raise( logger, "{} Value is Invalid. value should be Microsoft drive letter. e.g. D:\ " .format(val)) val = val.upper() if key_lower == 'iboxaddress': if re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', val): if not re.match(ip_address, val): log_n_raise(logger, "{} is Invalid IP address".format(val)) else: if not re.match(dns_name, val): log_n_raise(logger, "{} is Invalid InfiniBox DNS address".format(val)) return key, val
def is_cluster_online(): config = config_get(silent=True) cmd = execute_assert_success(['powershell', '-c', 'Get-ClusterGroup', '-name', config['FSRoleName'], '|', 'Select-Object', '-ExpandProperty', 'state']) if cmd.get_stdout().strip() != 'Online': log_n_raise(logger, "Cluster group {} NOT in Online state !! state is: {}".format(config['FSRoleName'], cmd.get_stdout().strip()))