示例#1
0
    def __init__(self, config, environment, namespace):
        '''
        The Creator will be passed the config and environment descriptors
        that are passed to the Deployment Manager when it is instantiated.
        '''
        self._config = config
        self._environment = environment
        self._namespace = namespace
        self._hdfs_client = HDFS(environment['webhdfs_host'],
                                 environment['webhdfs_port'],
                                 environment['webhdfs_user'])

        if 'yarn_resource_manager_host' in environment:
            self._yarn_resource_manager = "%s:%s" % (
                environment['yarn_resource_manager_host'],
                environment['yarn_resource_manager_port'])
        else:
            self._yarn_resource_manager = None

        if 'yarn_resource_manager_host_backup' in environment:
            self._yarn_resource_manager_backup = "%s:%s" % (
                environment['yarn_resource_manager_host_backup'],
                environment['yarn_resource_manager_port_backup'])
        else:
            self._yarn_resource_manager_backup = None
 def __init__(self, config, environment, service):
     self._config = config
     self._environment = environment
     self._service = service
     self._component_creators = {}
     self._name_regex = re.compile('')
     self._hdfs_client = HDFS(environment['webhdfs_host'],
                              environment['webhdfs_port'], 'hdfs')
 def __init__(self, hbase_host, hdfs_host, hdfs_user, hdfs_port, package_local_dir_path):
     self._hbase_host = hbase_host
     self._hdfs_user = hdfs_user
     self._hdfs_host = hdfs_host
     self._hdfs_port = hdfs_port
     self._hdfs_client = HDFS(hdfs_host, hdfs_port, hdfs_user)
     self._parser = PackageParser()
     self._table_name = 'platform_packages'
     self._package_hdfs_dir_path = "/user/pnda/application_packages"
     self._package_local_dir_path = package_local_dir_path
     if self._hbase_host is not None:
         connection = happybase.Connection(self._hbase_host)
         try:
             connection.create_table(self._table_name, {'cf': dict()})
             logging.debug("packages table created")
         except AlreadyExists:
             logging.debug("packages table exists")
         finally:
             connection.close()
示例#4
0
    def __init__(self, hbase_host, hdfs_host, hdfs_user, hdfs_port,
                 package_local_dir_path):
        self._hbase_host = hbase_host
        self._hdfs_user = hdfs_user
        self._hdfs_host = hdfs_host
        self._hdfs_port = hdfs_port
        self._hdfs_client = HDFS(hdfs_host, hdfs_port, hdfs_user)
        self._parser = PackageParser()
        self._table_name = 'platform_packages'
        self._dm_root_dir_path = "/pnda/system/deployment-manager"
        self._package_hdfs_dir_path = "%s/packages" % self._dm_root_dir_path
        self._package_local_dir_path = package_local_dir_path

        try:
            if hdfs_host is not None:
                self._hdfs_client.make_dir(self._dm_root_dir_path,
                                           permission=755)
                self._hdfs_client.make_dir(self._package_hdfs_dir_path,
                                           permission=600)
                logging.debug("packages HDFS folder created")
            else:
                logging.debug(
                    "not creating packages HDFS folder as it is not required")
        except AlreadyExists:
            logging.debug(
                "not creating packages HDFS folder as it already exists")

        if self._hbase_host is not None:
            connection = happybase.Connection(self._hbase_host)
            try:
                connection.create_table(self._table_name, {'cf': dict()})
                logging.debug("packages table created")
            except AlreadyExists:
                logging.debug("packages table exists")
            finally:
                connection.close()
class ApplicationCreator(object):
    def __init__(self, config, environment, service):
        self._config = config
        self._environment = environment
        self._service = service
        self._component_creators = {}
        self._name_regex = re.compile('')
        self._hdfs_client = HDFS(environment['webhdfs_host'],
                                 environment['webhdfs_port'], 'hdfs')

    def assert_application_properties(self, override_properties,
                                      default_properties):
        for component_type, component_properties in default_properties.items():
            creator = self._load_creator(component_type)
            creator.assert_application_properties(
                override_properties.get(component_type, {}),
                component_properties)

    def create_application(self, package_data_path, package_metadata,
                           application_name, property_overrides):

        logging.debug("create_application: %s", application_name)

        if not re.match('^[a-zA-Z0-9_-]+$', application_name):
            raise FailedCreation(
                'Application name %s may only contain a-z A-Z 0-9 - _' %
                application_name)

        user_name = property_overrides['user']
        try:
            pwd.getpwnam(user_name)
        except KeyError:
            raise FailedCreation(
                'User %s does not exist. Verify that this user account exists on the machine running the deployment manager.'
                % user_name)

        stage_path = self._stage_package(package_data_path)

        # create each class of components in the package, aggregating any
        # component specific return data for destruction
        create_metadata = {}
        try:
            for component_type, components in package_metadata[
                    'component_types'].items():
                creator = self._load_creator(component_type)
                result = creator.create_components(
                    stage_path, application_name, user_name, components,
                    property_overrides.get(component_type))
                create_metadata[component_type] = result
        finally:
            # clean up staged package data
            shutil.rmtree(stage_path)

        return create_metadata

    def destroy_application(self, application_name, application_create_data):

        logging.debug("destroy_application: %s %s", application_name,
                      application_create_data)

        app_hdfs_root = None
        for component_type, component_create_data in application_create_data.items(
        ):
            creator = self._load_creator(component_type)
            creator.destroy_components(application_name, component_create_data)
            if component_create_data and 'application_hdfs_root' in component_create_data[
                    0]:
                app_hdfs_root = component_create_data[0][
                    'application_hdfs_root']

        local_path = '/opt/%s/%s/' % (self._service, application_name)
        if os.path.isdir(local_path):
            os.rmdir(local_path)

        if app_hdfs_root is not None:
            self._hdfs_client.remove(app_hdfs_root, recursive=False)

    def start_application(self, application_name, application_create_data):

        logging.debug("start_application: %s %s", application_name,
                      application_create_data)

        for component_type, component_create_data in application_create_data.items(
        ):
            creator = self._load_creator(component_type)
            creator.start_components(application_name, component_create_data)

    def stop_application(self, application_name, application_create_data):

        logging.debug("stop_application: %s %s", application_name,
                      application_create_data)

        for component_type, component_create_data in application_create_data.items(
        ):
            creator = self._load_creator(component_type)
            creator.stop_components(application_name, component_create_data)

    def validate_package(self, package_name, package_metadata):

        logging.debug("validate_package: %s", json.dumps(package_metadata))

        result = {}
        self._validate_name(package_name, package_metadata)
        for component_type, component_metadata in package_metadata[
                'component_types'].items():
            creator = self._load_creator(component_type)
            validation_errors = creator.validate_components(component_metadata)
            if validation_errors:
                result[component_type] = validation_errors

        if result:
            raise FailedValidation(result)

    def _validate_name(self, package_name, package_metadata):

        parts = package_name.split('-')
        if len(parts) < 2:
            raise FailedValidation(
                "package name must be of the form name-version e.g. name-version.1.2.3 but found %s"
                % package_name)

        version_parts = parts[-1].split('.')
        if len(version_parts) < 3:
            raise FailedValidation(
                "version must be a three part major.minor.patch e.g. 1.2.3 but found %s"
                % parts[-1])

        if package_name != package_metadata['package_name']:
            raise FailedValidation(
                "package name must match name of enclosed folder but found %s and %s"
                % (package_name, package_metadata['package_name']))

    def get_application_runtime_details(self, application_name,
                                        application_create_data):

        logging.debug("get_application_runtime_details: %s %s",
                      application_name, application_create_data)

        details = {}
        details['yarn_applications'] = {}
        for component_type, component_create_data in application_create_data.items(
        ):
            creator = self._load_creator(component_type)
            type_details = creator.get_component_runtime_details(
                component_create_data)
            details['yarn_applications'].update(
                type_details['yarn_applications'])
        return details

    def _load_creator(self, component_type):

        logging.debug("_load_creator %s", component_type)

        creator = self._component_creators.get(component_type)

        if creator is None:

            module = '%s.%s' % (self._config['plugins_path'], component_type)
            cls = '%s%sCreator' % (component_type[0].upper(),
                                   component_type[1:])
            try:
                module = import_module("plugins.%s" % component_type)
                self._component_creators[component_type] = getattr(
                    module, cls)(self._config, self._environment,
                                 self._service)
                creator = self._component_creators[component_type]
            except ImportError as exception:
                logging.error(
                    'Unable to load Creator for component type "%s" [%s]',
                    component_type, exception)

        return creator

    def _stage_package(self, package_data_path):

        logging.debug("_stage_package")

        if not os.path.isdir(self._config['stage_root']):
            os.mkdir(self._config['stage_root'])

        tar = tarfile.open(package_data_path)
        stage_path = "%s/%s" % (self._config['stage_root'], uuid.uuid4())
        tar.extractall(path=stage_path)
        return stage_path
示例#6
0
class HbasePackageRegistrar(object):
    COLUMN_DEPLOY_STATUS = "cf:deploy_status"

    def __init__(self, hbase_host, hdfs_host, hdfs_user, hdfs_port,
                 package_local_dir_path):
        self._hbase_host = hbase_host
        self._hdfs_user = hdfs_user
        self._hdfs_host = hdfs_host
        self._hdfs_port = hdfs_port
        self._hdfs_client = HDFS(hdfs_host, hdfs_port, hdfs_user)
        self._parser = PackageParser()
        self._table_name = 'platform_packages'
        self._dm_root_dir_path = "/pnda/system/deployment-manager"
        self._package_hdfs_dir_path = "%s/packages" % self._dm_root_dir_path
        self._package_local_dir_path = package_local_dir_path

        try:
            if hdfs_host is not None:
                self._hdfs_client.make_dir(self._dm_root_dir_path,
                                           permission=755)
                self._hdfs_client.make_dir(self._package_hdfs_dir_path,
                                           permission=600)
                logging.debug("packages HDFS folder created")
            else:
                logging.debug(
                    "not creating packages HDFS folder as it is not required")
        except AlreadyExists:
            logging.debug(
                "not creating packages HDFS folder as it already exists")

        if self._hbase_host is not None:
            connection = happybase.Connection(self._hbase_host)
            try:
                connection.create_table(self._table_name, {'cf': dict()})
                logging.debug("packages table created")
            except AlreadyExists:
                logging.debug("packages table exists")
            finally:
                connection.close()

    def set_package(self, package_name, package_data_path, user):
        logging.debug("Storing %s", package_name)
        metadata = self._parser.get_package_metadata(package_data_path)
        metadata['user'] = user
        key, data = self.generate_record(metadata)
        self._write_to_hdfs(package_data_path, data['cf:package_data'])
        self._write_to_db(key, data)

    def set_package_deploy_status(self, package_name, deploy_status):
        """
        Stores information about the progress of the deploy process of the package
        :param deploy_status: the state to store
        """
        logging.debug("Storing state for %s: %s", package_name,
                      str(deploy_status))
        state_as_string = json.dumps(deploy_status)
        self._write_to_db(package_name,
                          {self.COLUMN_DEPLOY_STATUS: state_as_string})

    def delete_package(self, package_name):
        logging.debug("Deleting %s", package_name)
        package_data_hdfs_path = self._read_from_db(
            package_name, ['cf:package_data'])['cf:package_data']
        self._hdfs_client.remove(package_data_hdfs_path)
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            table.delete(package_name)
        finally:
            connection.close()

    def get_package_data(self, package_name):
        logging.debug("Reading %s", package_name)
        record = self._read_from_db(package_name, ['cf:package_data'])
        if not record:
            return None
        local_package_path = "%s/%s" % (self._package_local_dir_path,
                                        package_name)
        self._read_from_hdfs(record['cf:package_data'], local_package_path)
        return local_package_path

    def get_package_metadata(self, package_name):
        logging.debug("Reading %s", package_name)
        data = self._read_from_db(package_name,
                                  ['cf:metadata', 'cf:name', 'cf:version'])
        if not data:
            return None
        package_data = {
            key.docode("utf-8"): value.decode("utf-8")
            for key, value in data.items()
        }
        return {
            "metadata": json.loads(package_data["cf:metadata"]),
            "name": package_data["cf:name"],
            "version": package_data["cf:version"]
        }

    def package_exists(self, package_name):
        logging.debug("Checking %s", package_name)
        package_data = self._read_from_db(package_name, ['cf:name'])
        return len(package_data) > 0

    def get_package_deploy_status(self, package_name):
        """
        :param package_name: the package name to check status for
        :return: The last reported progress of the deploy process for the current package
        """
        logging.debug("Checking %s", package_name)
        package_data = self._read_from_db(package_name,
                                          columns=[self.COLUMN_DEPLOY_STATUS])
        if not package_data:
            return None
        # all status is stored as json, so parse it and return it
        deploy_status_as_string = package_data[self.COLUMN_DEPLOY_STATUS]
        return json.loads(deploy_status_as_string)

    def list_packages(self):
        logging.debug("List all packages")

        connection = None
        try:
            connection = happybase.Connection(self._hbase_host)
            table = connection.table(self._table_name)
            result = [key for key, _ in table.scan(columns=['cf:name'])]
        except Exception as exc:
            logging.debug(str(exc))
            raise FailedConnection('Unable to connect to the HBase master')
        finally:
            if connection:
                connection.close()
        return result

    def generate_record(self, metadata):
        return metadata["package_name"], {
            'cf:name':
            '-'.join(metadata["package_name"].split("-")[:-1]),
            'cf:version':
            metadata["package_name"].split("-")[-1],
            'cf:metadata':
            json.dumps(metadata),
            'cf:package_data':
            "%s/%s" % (self._package_hdfs_dir_path, metadata["package_name"])
        }

    def _read_from_db(self, key, columns):
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            data = table.row(key, columns=columns)
        finally:
            connection.close()
        return data

    def _read_from_hdfs(self, source_hdfs_path, dest_local_path):
        self._hdfs_client.stream_file_to_disk(source_hdfs_path,
                                              dest_local_path)

    def _write_to_db(self, key, data):
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            table.put(key, data)
        finally:
            connection.close()

    def _write_to_hdfs(self, source_local_path, dest_hdfs_path):
        with open(source_local_path, 'rb') as source_file:
            first = True
            chunk_size = 10 * 1024 * 1024
            data_chunk = source_file.read(chunk_size)
            while data_chunk:
                if first:
                    self._hdfs_client.create_file(data_chunk,
                                                  dest_hdfs_path,
                                                  permission=600)
                    first = False
                else:
                    self._hdfs_client.append_file(data_chunk, dest_hdfs_path)
                data_chunk = source_file.read(chunk_size)
class HbasePackageRegistrar(object):
    COLUMN_DEPLOY_STATUS = "cf:deploy_status"

    def __init__(self, hbase_host, hdfs_host, hdfs_user, hdfs_port, package_local_dir_path):
        self._hbase_host = hbase_host
        self._hdfs_user = hdfs_user
        self._hdfs_host = hdfs_host
        self._hdfs_port = hdfs_port
        self._hdfs_client = HDFS(hdfs_host, hdfs_port, hdfs_user)
        self._parser = PackageParser()
        self._table_name = 'platform_packages'
        self._package_hdfs_dir_path = "/user/pnda/application_packages"
        self._package_local_dir_path = package_local_dir_path
        if self._hbase_host is not None:
            connection = happybase.Connection(self._hbase_host)
            try:
                connection.create_table(self._table_name, {'cf': dict()})
                logging.debug("packages table created")
            except AlreadyExists:
                logging.debug("packages table exists")
            finally:
                connection.close()

    def set_package(self, package_name, package_data_path):
        logging.debug("Storing %s", package_name)
        metadata = self._parser.get_package_metadata(package_data_path)
        key, data = self.generate_record(metadata)
        self._write_to_hdfs(package_data_path, data['cf:package_data'])
        self._write_to_db(key, data)

    def set_package_deploy_status(self, package_name, deploy_status):
        """
        Stores information about the progress of the deploy process of the package
        :param deploy_status: the state to store
        """
        logging.debug("Storing state for %s: %s", package_name, str(deploy_status))
        state_as_string = json.dumps(deploy_status)
        self._write_to_db(package_name, {self.COLUMN_DEPLOY_STATUS: state_as_string})

    def delete_package(self, package_name):
        logging.debug("Deleting %s", package_name)
        package_data_hdfs_path = self._read_from_db(package_name, ['cf:package_data'])['cf:package_data']
        self._hdfs_client.remove(package_data_hdfs_path)
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            table.delete(package_name)
        finally:
            connection.close()

    def get_package_data(self, package_name):
        logging.debug("Reading %s", package_name)
        record = self._read_from_db(package_name, ['cf:package_data'])
        if len(record) == 0:
            return None
        local_package_path = "%s/%s" % (self._package_local_dir_path, package_name)
        self._read_from_hdfs(record['cf:package_data'], local_package_path)
        return local_package_path

    def get_package_metadata(self, package_name):
        logging.debug("Reading %s", package_name)
        package_data = self._read_from_db(
            package_name, ['cf:metadata', 'cf:name', 'cf:version'])
        if len(package_data) == 0:
            return None
        return {"metadata": json.loads(package_data["cf:metadata"]), "name": package_data[
            "cf:name"], "version": package_data["cf:version"]}

    def package_exists(self, package_name):
        logging.debug("Checking %s", package_name)
        package_data = self._read_from_db(package_name, ['cf:name'])
        return len(package_data) > 0

    def get_package_deploy_status(self, package_name):
        """
        :param package_name: the package name to check status for
        :return: The last reported progress of the deploy process for the current package
        """
        logging.debug("Checking %s", package_name)
        package_data = self._read_from_db(package_name, columns=[self.COLUMN_DEPLOY_STATUS])
        if len(package_data) == 0:
            return None
        # all status is stored as json, so parse it and return it
        deploy_status_as_string = package_data[self.COLUMN_DEPLOY_STATUS]
        return json.loads(deploy_status_as_string)

    def list_packages(self):
        logging.debug("List all packages")

        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            result = [key for key, _ in table.scan(columns=['cf:name'])]
        finally:
            connection.close()
        return result

    def generate_record(self, metadata):
        return metadata["package_name"], {
            'cf:name': '-'.join(metadata["package_name"].split("-")[:-1]),
            'cf:version': metadata["package_name"].split("-")[-1],
            'cf:metadata': json.dumps(metadata),
            'cf:package_data': "%s/%s" %  (self._package_hdfs_dir_path, metadata["package_name"])
        }

    def _read_from_db(self, key, columns):
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            data = table.row(key, columns=columns)
        finally:
            connection.close()
        return data

    def _read_from_hdfs(self, source_hdfs_path, dest_local_path):
        self._hdfs_client.stream_file_to_disk(source_hdfs_path, dest_local_path)

    def _write_to_db(self, key, data):
        connection = happybase.Connection(self._hbase_host)
        try:
            table = connection.table(self._table_name)
            table.put(key, data)
        finally:
            connection.close()

    def _write_to_hdfs(self, source_local_path, dest_hdfs_path):
        with open(source_local_path, 'rb') as source_file:
            first = True
            chunk_size = 10*1024*1024
            data_chunk = source_file.read(chunk_size)
            while data_chunk:
                if first:
                    self._hdfs_client.create_file(data_chunk, dest_hdfs_path)
                    first = False
                else:
                    self._hdfs_client.append_file(data_chunk, dest_hdfs_path)
                data_chunk = source_file.read(chunk_size)
class Creator(object):
    '''
    Base Functionality for Creator classes
    '''
    def __init__(self, config, environment, namespace):
        '''
        The Creator will be passed the config and environment descriptors
        that are passed to the Deployment Manager when it is instantiated.
        '''
        self._config = config
        self._environment = environment
        self._namespace = namespace
        self._hdfs_client = HDFS(environment['webhdfs_host'],
                                 environment['webhdfs_port'], 'hdfs')

        if 'yarn_resource_manager_host' in environment:
            self._yarn_resource_manager = "%s:%s" % (
                environment['yarn_resource_manager_host'],
                environment['yarn_resource_manager_port'])
        else:
            self._yarn_resource_manager = None

        if 'yarn_resource_manager_host_backup' in environment:
            self._yarn_resource_manager_backup = "%s:%s" % (
                environment['yarn_resource_manager_host_backup'],
                environment['yarn_resource_manager_port_backup'])
        else:
            self._yarn_resource_manager_backup = None

    def validate_component(self, components):
        '''
        Validates components of the package of given component type

        components - a list of component maps reflecting the structure of the
                 components within the package and following this example.
                 component_detail, component_path and component_name are
                 guaranteed to be present, component_detail will vary according
                 to the contents of the package. The validation function can
                 do anything but it is sensible to check for presence of key
                 files, for example. Since the staging path is present in the
                 config passed to the Creator it is possible to load the actual
                 file data and execute more complex validation if desired.

        returns - Array of messages indication what is wrong, 0 length array for no errors

        '''
        pass

    def create_component(self, staged_component_path, application_name,
                         user_name, component, properties):
        '''
        Creates component of the package of given component type

        application_name - name of the application
        user_name - user to run the application as
        components - a list of component maps following above example structure
        properties - a full map of properties that can be used as needed

        returns - create data, which can be anything but will be associated
              with this application creation by the Deployment Manager. Therefore
              it makes sense to include enough information to be able to
              implement destroy_components.
        '''
        pass

    def assert_application_properties(self, override_properties,
                                      default_properties):
        '''
        Assert application properties before creating the application

        override_properties - properties overrided by user
        default_properties - default properties present in package
        '''
        pass

    def destroy_component(self, application_name, create_data):
        '''
        Destroys component of the package of given component type

        application_name - name of the application
        create_data - as per above explanation, the data returned from the
                      corresponding create operation, which should be
                      sufficient to execute a clean destruction.
        '''
        pass

    def start_component(self, application_name, start_data):
        '''
        starts component of the package of given component type

        application_name - name of the application
        create_data - as per above explanation, the data returned from the
                      corresponding create operation, which should be
                      sufficient to execute start command.
        '''
        pass

    def stop_component(self, application_name, stop_data):
        '''
        stops component of the package of given component type

        application_name - name of the application
        create_data - as per above explanation, the data returned from the
                      corresponding create operation, which should be
                      sufficient to execute stop command.
        '''
        pass

    def get_component_type(self):
        '''
        returns a name for the component type
        '''
        pass

    def _instantiate_properties(self, application_name, user_name, component,
                                property_overrides):
        logging.debug("_instantiate_properties %s %s", component,
                      property_overrides)

        component_properties = {}
        if 'properties.json' in component['component_detail']:
            component_properties = component['component_detail'][
                'properties.json']

        props = collections.OrderedDict()
        for prop in self._environment:
            props['environment_' + prop] = self._environment[prop]
        for prop in component_properties:
            props['component_' + prop] = component_properties[prop]
        for prop in property_overrides:
            props['component_' + prop] = property_overrides[prop]

        props['component_application'] = application_name
        props['component_name'] = component['component_name']
        props['component_job_name'] = '%s-%s-job' % (
            props['component_application'], props['component_name'])
        props[
            'application_hdfs_root'] = '/pnda/system/deployment-manager/applications/%s/%s' % (
                user_name, application_name)
        props['component_hdfs_root'] = '%s/%s' % (
            props['application_hdfs_root'], component['component_name'])
        props['application_user'] = user_name
        return props

    def _fill_properties(self, local_file, props):
        with open(local_file, "r") as myfile:
            file_contents = myfile.read()

        new_file_contents = string.Template(file_contents).safe_substitute(
            props)

        with open(local_file, "w") as myfile:
            myfile.write(new_file_contents)

    def _auto_fill_app_properties(self, staged_component_path, props):
        app_properties_file_path = '%s/application.properties' % staged_component_path
        with open(app_properties_file_path, "a") as app_properties_file:
            if 'component_no_auto_props' not in props:
                app_properties_file.write('\n')
                for prop in props:
                    app_properties_file.write(
                        '%s=%s\n' % (prop.replace('_', '.', 1), props[prop]))

    def _create_optional_descriptors(self, staged_component_path, component,
                                     properties):
        result = {}
        if 'hbase.json' in component['component_detail']:
            logging.debug("creating hbase.json")
            self._fill_properties(
                '%s/%s' % (staged_component_path, 'hbase.json'), properties)
            hbase_descriptor.create(
                '%s/%s' % (staged_component_path, 'hbase.json'),
                self._environment)

        if 'hdfs.json' in component['component_detail']:
            logging.debug("creating hdfs.json")
            descriptor_path = '%s/%s' % (staged_component_path, 'hdfs.json')
            self._fill_properties(descriptor_path, properties)
            with open(descriptor_path) as descriptor_file:
                contents = descriptor_file.read()
                hdfs_descriptor = json.loads(contents)
            for element in hdfs_descriptor:
                properties['hdfspath_' + element['name']] = element['path']
                self._hdfs_client.make_dir(element['path'])

            result['hdfs.json'] = hdfs_descriptor

        if 'opentsdb.json' in component['component_detail']:
            logging.debug("creating opentsdb.json")
            self._fill_properties(
                '%s/%s' % (staged_component_path, 'opentsdb.json'), properties)
            opentsdb_descriptor.create(
                '%s/%s' % (staged_component_path, 'opentsdb.json'),
                self._environment)

        return result

    def _destroy_optional_descriptors(self, descriptors):
        result = {}

        if 'hdfs.json' in descriptors:
            logging.debug("destroying hdfs.json")
            hdfs_descriptor = descriptors['hdfs.json']
            for element in hdfs_descriptor:
                if element['delete_on_undeploy'] > 0:
                    dir_count = element['delete_on_undeploy']
                    while dir_count > 0:
                        dir_count = dir_count - 1
                        element['path'] = element['path'][:element['path'].
                                                          rfind('/')]
                    self._hdfs_client.remove(element['path'], recursive=True)

        return result

    def create_components(self, stage_path, application_name, user_name,
                          components, components_overrides):
        results = []
        for component_name, component in components.items():
            staged_component_path = '%s/%s' % (stage_path,
                                               component['component_path'])
            overrides = components_overrides.get(
                component_name) if components_overrides is not None else {}
            overrides = {} if overrides is None else overrides
            merged_props = self._instantiate_properties(
                application_name, user_name, component, overrides)
            descriptor_result = self._create_optional_descriptors(
                staged_component_path, component, merged_props)
            self._auto_fill_app_properties(staged_component_path, merged_props)
            result = self.create_component(staged_component_path,
                                           application_name, user_name,
                                           component, merged_props)
            result['component_name'] = component_name
            result['application_hdfs_root'] = merged_props[
                'application_hdfs_root']
            result['component_job_name'] = merged_props['component_job_name']
            result['descriptors'] = descriptor_result
            results.append(result)
        return results

    def destroy_components(self, application_name, create_data):
        for single_component_data in create_data:
            self._destroy_optional_descriptors(
                single_component_data['descriptors'])
            self.destroy_component(application_name, single_component_data)
        return None

    def start_components(self, application_name, start_data):
        for single_component_data in start_data:
            self.start_component(application_name, single_component_data)
        return None

    def stop_components(self, application_name, stop_data):
        for single_component_data in stop_data:
            self.stop_component(application_name, single_component_data)
        return None

    def validate_components(self, components):
        logging.debug("validate_components: %s", components)
        result = {}
        for component_name, component in components.items():
            validation_errors = self.validate_component(component)
            if validation_errors:
                result[component_name] = validation_errors
        return result

    def get_component_runtime_details(self, create_data):
        logging.debug("get_component_runtime_details: %s", create_data)

        details = {}
        yarn_applications = {}
        details['yarn_applications'] = yarn_applications

        all_yarn_applications = self._get_yarn_applications()
        if all_yarn_applications is not None:
            for single_component_data in create_data:
                app_info = self._find_yarn_app_info(
                    all_yarn_applications,
                    single_component_data['component_job_name'])
                if app_info is not None:
                    job_key = '%s-%s' % (self.get_component_type(
                    ), single_component_data['component_name'])
                    yarn_applications[job_key] = {
                        "component": single_component_data['component_name'],
                        "type": self.get_component_type(),
                        "yarn-id": app_info['id'],
                        "yarn-start-time": app_info['startedTime'],
                        "yarn-state": app_info['state']
                    }
        else:
            logging.error(
                'Failed to query application list from any resource manager')

        return details

    def _get_yarn_applications_from_rm(self, resource_manager):
        result = None
        logging.debug('Querying list of yarn applications from %s',
                      resource_manager)
        try:
            url = 'http://%s/ws/v1/cluster/apps' % resource_manager
            result = requests.get(url, headers={
                'Accept': 'application/json'
            }).json()
        except:
            logging.info('Failed to query application list from %s', url)

        return result

    def _get_yarn_applications(self):
        result = self._get_yarn_applications_from_rm(
            self._yarn_resource_manager)
        if result is None and self._yarn_resource_manager_backup is not None:
            result = self._get_yarn_applications_from_rm(
                self._yarn_resource_manager_backup)
        return result

    def _get_yarn_start_time(self, app_info):
        try:
            return int(app_info['startedTime'])
        except:
            return 0

    def _find_yarn_app_info(self, all_yarn_applications, job_name):
        result = None
        if 'apps' in all_yarn_applications and 'app' in all_yarn_applications[
                'apps']:
            for app in all_yarn_applications['apps']['app']:
                if app['name'] == job_name:
                    if result is None or self._get_yarn_start_time(
                            app) > self._get_yarn_start_time(result):
                        result = app
        return result