def __init__(self, config, contact_point=None):
        cassandra_config = config.cassandra
        self._start_cmd = shlex.split(cassandra_config.start_cmd)
        self._stop_cmd = shlex.split(cassandra_config.stop_cmd)
        self._is_ccm = int(shlex.split(cassandra_config.is_ccm)[0])
        self._os_has_systemd = self._has_systemd()
        self._nodetool = Nodetool(cassandra_config)
        logging.warning('is ccm : {}'.format(self._is_ccm))

        config_reader = CassandraConfigReader(cassandra_config.config_file)
        self._cassandra_config_file = cassandra_config.config_file
        self._root = config_reader.root
        self._commitlog_path = config_reader.commitlog_directory
        self._saved_caches_path = config_reader.saved_caches_directory
        self._hostname = contact_point if contact_point is not None else config_reader.listen_address
        self._cql_session_provider = CqlSessionProvider([self._hostname],
                                                        cassandra_config)

        self._storage_port = config_reader.storage_port
        self._native_port = config_reader.native_port
        self._rpc_port = config_reader.rpc_port
        self.seeds = config_reader.seeds

        self.grpc_config = config.grpc
        self.kubernetes_config = config.kubernetes
        self.snapshot_service = SnapshotService(config=config).snapshot_service
class Cassandra(object):

    SNAPSHOT_PATTERN = '*/*/snapshots/{}'
    SNAPSHOT_PREFIX = 'medusa-'

    def __init__(self, config, contact_point=None):
        cassandra_config = config.cassandra
        self._start_cmd = shlex.split(cassandra_config.start_cmd)
        self._stop_cmd = shlex.split(cassandra_config.stop_cmd)
        self._is_ccm = int(shlex.split(cassandra_config.is_ccm)[0])
        self._os_has_systemd = self._has_systemd()
        self._nodetool = Nodetool(cassandra_config)
        logging.warning('is ccm : {}'.format(self._is_ccm))

        config_reader = CassandraConfigReader(cassandra_config.config_file)
        self._cassandra_config_file = cassandra_config.config_file
        self._root = config_reader.root
        self._commitlog_path = config_reader.commitlog_directory
        self._saved_caches_path = config_reader.saved_caches_directory
        self._hostname = contact_point if contact_point is not None else config_reader.listen_address
        self._cql_session_provider = CqlSessionProvider([self._hostname],
                                                        cassandra_config)

        self._storage_port = config_reader.storage_port
        self._native_port = config_reader.native_port
        self._rpc_port = config_reader.rpc_port
        self.seeds = config_reader.seeds

        self.grpc_config = config.grpc
        self.kubernetes_config = config.kubernetes
        self.snapshot_service = SnapshotService(config=config).snapshot_service

    def _has_systemd(self):
        try:
            result = subprocess.run(['systemctl', '--version'],
                                    stdout=PIPE,
                                    stderr=PIPE)
            logging.debug(
                'This server has systemd: {}'.format(result.returncode == 0))
            return result.returncode == 0
        except (AttributeError, FileNotFoundError):
            # AttributeError is thrown when subprocess.run is not found, which happens on Trusty
            # Trusty doesn't have systemd, so the semantics of this code still hold
            logging.debug('This server has systemd: False')
            return False

    def new_session(self):
        return self._cql_session_provider.new_session()

    @property
    def root(self):
        return self._root

    @property
    def commit_logs_path(self):
        return self._commitlog_path

    @property
    def saved_caches_path(self):
        return self._saved_caches_path

    @property
    def hostname(self):
        return self._hostname

    @property
    def storage_port(self):
        return self._storage_port

    @property
    def native_port(self):
        return self._native_port

    @property
    def rpc_port(self):
        return self._rpc_port

    class Snapshot(object):
        def __init__(self, parent, tag):
            self._parent = parent
            self._tag = tag

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            logging.debug('Cleaning up snapshot')
            self.delete()

        @property
        def cassandra(self):
            return self._parent

        @property
        def tag(self):
            return self._tag

        @property
        def root(self):
            return self._parent.root

        def find_dirs(self):
            return [
                SnapshotPath(pathlib.Path(snapshot_dir),
                             *snapshot_dir.relative_to(self.root).parts[:2])
                for snapshot_dir in self.root.glob(
                    Cassandra.SNAPSHOT_PATTERN.format(self._tag))
                if (snapshot_dir.is_dir() and snapshot_dir.parts[-4] not in
                    CqlSession.EXCLUDED_KEYSPACES)
            ]

        def delete(self):
            self._parent.delete_snapshot(self._tag)

        def __repr__(self):
            return '{}<{}>'.format(self.__class__.__qualname__, self._tag)

    def create_snapshot(self, backup_name):
        tag = "{}{}".format(self.SNAPSHOT_PREFIX, backup_name)
        if not self.snapshot_exists(tag):
            self.snapshot_service.create_snapshot(tag=tag)

        return Cassandra.Snapshot(self, tag)

    def delete_snapshot(self, tag):
        if self.snapshot_exists(tag):
            self.snapshot_service.delete_snapshot(tag=tag)

    def list_snapshotnames(self):
        return {
            snapshot.name
            for snapshot in self.root.glob(self.SNAPSHOT_PATTERN.format('*'))
            if snapshot.is_dir()
        }

    def get_snapshot(self, tag):
        if any(self.root.glob(self.SNAPSHOT_PATTERN.format(tag))):
            return Cassandra.Snapshot(self, tag)

        raise KeyError('Snapshot {} does not exist'.format(tag))

    def snapshot_exists(self, tag):
        for snapshot in self.root.glob(self.SNAPSHOT_PATTERN.format('*')):
            if snapshot.is_dir() and snapshot.name == tag:
                return True
        return False

    def create_snapshot_command(self, backup_name):
        """
        :param backup_name: string name of the medusa backup
        :return: Array representation of a command to create a snapshot
        """
        tag = '{}{}'.format(self.SNAPSHOT_PREFIX, backup_name)
        if self._is_ccm == 1:
            cmd = 'ccm node1 nodetool \"snapshot -t {}\"'.format(tag)
        else:
            cmd = self._nodetool.nodetool + ['snapshot', '-t', tag]
        return cmd

    def delete_snapshot_command(self, tag):
        """
        :param tag: string snapshot name
        :return: Array repesentation of a command to delete a snapshot
        """
        if self._is_ccm == 1:
            cmd = 'ccm node1 nodetool \"clearsnapshot -t {}\"'.format(tag)
        else:
            cmd = self._nodetool.nodetool + ['clearsnapshot', '-t', tag]
        return cmd

    def _columnfamily_path(self, keyspace_name, columnfamily_name, cf_id):
        root = pathlib.Path(self._root)
        keyspace_path = root / keyspace_name / columnfamily_name

        if keyspace_path.exists() and keyspace_path.is_dir():
            return keyspace_path
        else:
            # Notice: Cassandra use dashes in the cf_id in the system table,
            # but not in the directory names
            directory_postfix = str(cf_id).replace('-', '')
            return keyspace_path.with_name('{}-{}'.format(
                columnfamily_name, directory_postfix))

    def _full_columnfamily_name(self, keyspace_name, columnfamily_name, cf_id):
        root = pathlib.Path(self._root)
        keyspace_path = root / keyspace_name / columnfamily_name

        if keyspace_path.exists() and keyspace_path.is_dir():
            return columnfamily_name
        else:
            # Notice: Cassandra use dashes in the cf_id in the system table,
            # but not in the directory names
            directory_postfix = str(cf_id).replace('-', '')
            return '{}-{}'.format(columnfamily_name, directory_postfix)

    def schema_path_mapping(self):
        def _full_cf_name(row):
            return self._full_columnfamily_name(row.keyspace_name,
                                                row.columnfamily_name,
                                                row.cf_id)

        def _full_cf_path(row):
            return self._columnfamily_path(row.keyspace_name,
                                           row.columnfamily_name, row.cf_id)

        with self._cql_session_provider.new_session() as session:
            return {(row.keyspace_name, _full_cf_name(row)): _full_cf_path(row)
                    for row in session.schema_path_mapping()}

    def shutdown(self):
        try:
            subprocess.check_output(self._stop_cmd)
        except subprocess.CalledProcessError:
            logging.debug('Cassandra is already down on {}'.format(
                self._hostname))
            return

    def start_with_implicit_token(self):
        cmd = self._start_cmd
        logging.debug('Starting Cassandra with {}'.format(cmd))
        subprocess.check_output(cmd)

    def start(self, token_list):
        if self._is_ccm == 0:
            self.replaceTokensInCassandraYamlAndDisableBootstrap(token_list)
            cmd = '{}'.format(' '.join(
                shlex.quote(x) for x in self._start_cmd))
            logging.debug('Starting Cassandra with {}'.format(cmd))
            # run the command using 'shell=True' option
            # to interpret the string command well
            subprocess.check_output(cmd, shell=True)
        else:
            subprocess.check_output(self._start_cmd, shell=True)

    def replaceTokensInCassandraYamlAndDisableBootstrap(self, token_list):
        initial_token_line_found = False
        auto_bootstrap_line_found = False
        for line in fileinput.input(self._cassandra_config_file, inplace=True):
            if (line.startswith("initial_token:")):
                initial_token_line_found = True
                print('initial_token: {}'.format(','.join(token_list)),
                      end='\n')
            elif (line.startswith("num_tokens:")):
                print('num_tokens: {}'.format(len(token_list)), end='\n')
            elif (line.startswith("auto_bootstrap:")):
                auto_bootstrap_line_found = True
                print('auto_bootstrap: false', end='\n')
            else:
                print('{}'.format(line), end='')

        if (not initial_token_line_found):
            with open(self._cassandra_config_file, "a") as cassandra_yaml:
                cassandra_yaml.write('\ninitial_token: {}'.format(
                    ','.join(token_list)))

        if (not auto_bootstrap_line_found):
            with open(self._cassandra_config_file, "a") as cassandra_yaml:
                cassandra_yaml.write('\nauto_bootstrap: false')