Beispiel #1
0
def _bf_answer_obj(
    session,
    question_str,
    parameters_str,
    question_name,
    background,
    snapshot,
    reference_snapshot,
    extra_args,
):
    # type: (Session, str, str, str, bool, str, Optional[str], Optional[Dict[str, Any]]) -> Union[Answer, str]
    json.loads(parameters_str)  # a syntactic check for parametersStr
    if not question_name:
        question_name = Options.default_question_prefix + "_" + get_uuid()

    # Upload the question
    json_data = workhelper.get_data_upload_question(session, question_name,
                                                    question_str,
                                                    parameters_str)
    resthelper.get_json_response(session, CoordConsts.SVC_RSC_UPLOAD_QUESTION,
                                 json_data)

    # Answer the question
    work_item = workhelper.get_workitem_answer(session, question_name,
                                               snapshot, reference_snapshot)
    workhelper.execute(work_item, session, background, extra_args)

    if background:
        return work_item.id

    # get the answer
    return session.get_answer(question_name, snapshot, reference_snapshot)
Beispiel #2
0
    def _init_snapshot(self, upload, name=None, overwrite=False,
                       background=False,
                       extra_args=None):
        # type: (Union[str, IO], Optional[str], bool, bool, Optional[Dict[str, Any]]) -> Union[str, Dict[str, str]]
        if self.network is None:
            self.set_network()

        if name is None:
            name = Options.default_snapshot_prefix + get_uuid()
        validate_name(name)

        if name in self.list_snapshots():
            if overwrite:
                self.delete_snapshot(name)
            else:
                raise ValueError(
                    'A snapshot named ''{}'' already exists in network ''{}''. '
                    'Use overwrite = True if you want to overwrite the '
                    'existing snapshot'.format(name, self.network))

        if isinstance(upload, six.string_types):
            self.__init_snapshot_from_file(name, upload)
        else:
            if not zipfile.is_zipfile(upload):
                raise ValueError("The provided data is not a valid zip file")
            # upload is an IO-like object already
            self.__init_snapshot_from_io(name, upload)

        return self._parse_snapshot(name, background, extra_args)
Beispiel #3
0
        def constructor(self, *args, **kwargs):
            """Create a new question."""
            # Reject positional args; this way is PY2-compliant
            if args:
                raise TypeError("Please use keyword arguments")

            # Call super (i.e., QuestionBase)
            super(new_cls, self).__init__(new_cls.template, new_cls.session)

            # Update well-known params, if passed in
            if "exclusions" in kwargs:
                self._dict['exclusions'] = kwargs.get("exclusions")
            if "question_name" in kwargs:
                self._dict['instance']['instanceName'] = kwargs.get(
                    "question_name")
            else:
                self._dict['instance']['instanceName'] = ("__{}_{}".format(
                    self._dict['instance']['instanceName'], get_uuid()))

            # Validate that we are not accepting invalid kwargs/variables
            instance_vars = self._dict['instance'].get('variables', {})
            allowed_kwargs = set(instance_vars)
            allowed_kwargs.update(additional_kwargs)
            var_difference = set(kwargs.keys()).difference(allowed_kwargs)
            if var_difference:
                raise QuestionValidationException(
                    "Received unsupported parameters/variables: {}".format(
                        var_difference))
            # Set question-specific parameters
            for var_name, var_value in kwargs.items():
                if var_name not in additional_kwargs:
                    instance_vars[var_name]['value'] = var_value
Beispiel #4
0
        def constructor(self, question_name=None,
                        exclusions=None, **kwargs):
            """Create a new question."""
            # Call super (i.e., QuestionBase)
            super(new_cls, self).__init__(new_cls.template)

            # Update well-known params, if passed in
            if exclusions is not None:
                self._dict['exclusions'] = exclusions
            if question_name:
                self._dict['instance']['instanceName'] = question_name
            else:
                self._dict['instance']['instanceName'] = (
                    "__{}_{}".format(
                        self._dict['instance']['instanceName'], get_uuid()))

            # Validate that we are not accepting invalid kwargs/variables
            instance_vars = self._dict['instance'].get('variables', {})
            var_difference = set(kwargs.keys()).difference(instance_vars)
            if var_difference:
                raise QuestionValidationException(
                    "Received unsupported parameters/variables: {}".format(
                        var_difference))
            # Set question-specific parameters
            for var_name, var_value in kwargs.items():
                instance_vars[var_name]['value'] = var_value
Beispiel #5
0
    def _fork_snapshot(
        self,
        base_name,
        name=None,
        overwrite=False,
        background=False,
        deactivate_interfaces=None,
        deactivate_nodes=None,
        restore_interfaces=None,
        restore_nodes=None,
        add_files=None,
        extra_args=None,
    ):
        # type: (str, Optional[str], bool, bool, Optional[List[Interface]], Optional[List[str]], Optional[List[Interface]], Optional[List[str]], Optional[str], Optional[Dict[str, Any]]) -> Union[str, Dict, None]
        self._check_network()

        if name is None:
            name = Options.default_snapshot_prefix + get_uuid()
        validate_name(name)

        if name in self.list_snapshots():
            if overwrite:
                self.delete_snapshot(name)
            else:
                raise ValueError(
                    "A snapshot named "
                    "{}"
                    " already exists in network "
                    "{}"
                    ". "
                    "Use overwrite = True if you want to overwrite the "
                    "existing snapshot".format(name, self.network)
                )

        encoded_file = None
        if add_files is not None:
            file_to_send = add_files
            if os.path.isdir(add_files):
                temp_zip_file = tempfile.NamedTemporaryFile()
                zip_dir(add_files, temp_zip_file)
                file_to_send = temp_zip_file.name

            if os.path.isfile(file_to_send):
                with open(file_to_send, "rb") as f:
                    encoded_file = base64.b64encode(f.read()).decode("ascii")

        json_data = {
            "snapshotBase": base_name,
            "snapshotNew": name,
            "deactivateInterfaces": deactivate_interfaces,
            "deactivateNodes": deactivate_nodes,
            "restoreInterfaces": restore_interfaces,
            "restoreNodes": restore_nodes,
            "zipFile": encoded_file,
        }
        restv2helper.fork_snapshot(self, json_data)

        return self._parse_snapshot(name, background, extra_args)
Beispiel #6
0
def bf_init_snapshot(upload,
                     name=None,
                     overwrite=False,
                     background=False,
                     extra_args=None):
    # type: (str, Optional[str], bool, bool, Optional[Dict[str, Any]]) -> Union[str, Dict[str, str]]
    """Initialize a new snapshot.

    :param upload: snapshot to upload
    :type upload: zip file or directory
    :param name: name of the snapshot to initialize
    :type name: string
    :param overwrite: whether or not to overwrite an existing snapshot with the
       same name
    :type overwrite: bool
    :param background: whether or not to run the task in the background
    :type background: bool
    :param extra_args: extra arguments to be passed to the parse command. See bf_session.additionalArgs.
    :type extra_args: dict
    :return: name of initialized snapshot, or JSON dictionary of task status if background=True
    :rtype: Union[str, Dict]
    """
    if bf_session.network is None:
        bf_set_network()

    if name is None:
        name = Options.default_snapshot_prefix + get_uuid()
    validate_name(name)

    if name in bf_list_snapshots():
        if overwrite:
            bf_delete_snapshot(name)
        else:
            raise ValueError('A snapshot named '
                             '{}'
                             ' already exists in network '
                             '{}'
                             ''.format(name, bf_session.network))

    file_to_send = upload
    if os.path.isdir(upload):
        temp_zip_file = tempfile.NamedTemporaryFile()
        zip_dir(upload, temp_zip_file)
        file_to_send = temp_zip_file.name

    json_data = workhelper.get_data_upload_snapshot(bf_session, name,
                                                    file_to_send)
    resthelper.get_json_response(bf_session,
                                 CoordConsts.SVC_RSC_UPLOAD_SNAPSHOT,
                                 json_data)

    return _parse_snapshot(name, background, extra_args)
Beispiel #7
0
    def _init_snapshot(self,
                       upload,
                       name=None,
                       overwrite=False,
                       background=False,
                       extra_args=None):
        # type: (str, Optional[str], bool, bool, Optional[Dict[str, Any]]) -> Union[str, Dict[str, str]]
        if self.network is None:
            self.set_network()

        if name is None:
            name = Options.default_snapshot_prefix + get_uuid()
        validate_name(name)

        if name in self.list_snapshots():
            if overwrite:
                self.delete_snapshot(name)
            else:
                raise ValueError(
                    'A snapshot named '
                    '{}'
                    ' already exists in network '
                    '{}'
                    '. '
                    'Use overwrite = True if you want to overwrite the '
                    'existing snapshot'.format(name, self.network))

        file_to_send = upload
        tmp_file_name = None  # type: Optional[Text]
        if os.path.isdir(upload):
            # delete=False because we re-open for reading
            with tempfile.NamedTemporaryFile(delete=False) as temp_zip_file:
                zip_dir(upload, temp_zip_file)
                tmp_file_name = file_to_send = temp_zip_file.name

        with open(file_to_send, 'rb') as fd:
            json_data = workhelper.get_data_upload_snapshot(self, name, fd)

            resthelper.get_json_response(self,
                                         CoordConsts.SVC_RSC_UPLOAD_SNAPSHOT,
                                         json_data)
        # Cleanup tmp file if we made one
        if tmp_file_name is not None:
            try:
                os.remove(tmp_file_name)
            except (OSError, IOError):
                # If we can't delete the file for some reason, let it be,
                # no need to crash initialization
                pass

        return self._parse_snapshot(name, background, extra_args)
Beispiel #8
0
def _bf_init_snapshot(upload, name, overwrite, background):
    if bf_session.network is None:
        bf_set_network()

    if name is None:
        name = Options.default_snapshot_prefix + get_uuid()
    validate_name(name)

    if name in bf_list_snapshots():
        if overwrite:
            bf_delete_snapshot(name)
        else:
            raise ValueError('A snapshot named '
                             '{}'
                             ' already exists in network '
                             '{}'
                             ''.format(name, bf_session.network))

    file_to_send = upload
    if os.path.isdir(upload):
        tempFile = tempfile.NamedTemporaryFile()
        zip_dir(upload, tempFile)
        file_to_send = tempFile.name

    json_data = workhelper.get_data_upload_snapshot(bf_session, name,
                                                    file_to_send)
    resthelper.get_json_response(bf_session,
                                 CoordConsts.SVC_RSC_UPLOAD_SNAPSHOT,
                                 json_data)

    bf_session.baseSnapshot = name
    work_item = workhelper.get_workitem_parse(bf_session, name)
    parse_execute = workhelper.execute(work_item,
                                       bf_session,
                                       background=background)
    if not background:
        status = parse_execute["status"]
        if WorkStatusCode(status) != WorkStatusCode.TERMINATEDNORMALLY:
            bf_session.baseSnapshot = None
            bf_logger.info("Default snapshot is now unset")
        else:
            bf_logger.info("Default snapshot is now set to %s",
                           bf_session.baseSnapshot)
    return parse_execute
Beispiel #9
0
    def set_network(
        self, name: Optional[str] = None, prefix: str = Options.default_network_prefix
    ) -> str:
        """
        Configure the network used for analysis.

        :param name: name of the network to set. If `None`, a name will be generated
        :type name: str
        :param prefix: prefix to prepend to auto-generated network names if name is empty
        :type name: str

        :return: name of the configured network
        :rtype: str
        :raises BatfishException: if configuration fails
        """
        if name is None:
            name = prefix + get_uuid()
        validate_name(name, "network")

        try:
            net = restv2helper.get_network(self, name)
            self.network = str(net["name"])
            return self.network
        except HTTPError as e:
            if e.response.status_code != 404:
                raise BatfishException("Unknown error accessing network", e)

        json_data = workhelper.get_data_init_network(self, name)
        json_response = resthelper.get_json_response(
            self, CoordConsts.SVC_RSC_INIT_NETWORK, json_data
        )

        network_name = json_response.get(CoordConsts.SVC_KEY_NETWORK_NAME)
        if network_name is None:
            raise BatfishException(
                "Network initialization failed. Server response: {}".format(
                    json_response
                )
            )

        self.network = str(network_name)
        return self.network
Beispiel #10
0
def _bf_answer_obj(question_str, parameters_str, question_name,
                   background, snapshot, reference_snapshot):
    # type: (str, str, str, bool, str, Optional[str]) -> Union[str, Dict]

    json.loads(parameters_str)  # a syntactic check for parametersStr
    if not question_name:
        question_name = Options.default_question_prefix + "_" + get_uuid()

    # Upload the question
    json_data = workhelper.get_data_upload_question(bf_session, question_name,
                                                    question_str,
                                                    parameters_str)
    resthelper.get_json_response(bf_session,
                                 CoordConsts.SVC_RSC_UPLOAD_QUESTION, json_data)

    # Answer the question
    work_item = workhelper.get_workitem_answer(bf_session, question_name,
                                               snapshot, reference_snapshot)
    answer_dict = workhelper.execute(work_item, bf_session, background)
    if background:
        return work_item.id
    return answer.from_string(answer_dict["answer"])
Beispiel #11
0
def bf_set_network(name=None, prefix=Options.default_network_prefix):
    # type: (str, str) -> str
    """
    Configure the network used for analysis.

    :param name: name of the network to set. If `None`, a name will be generated using prefix.
    :type name: string
    :param prefix: prefix to prepend to auto-generated network names if name is empty
    :type name: string

    :return: The name of the configured network, if configured successfully.
    :rtype: string
    :raises BatfishException: if configuration fails
    """
    if name is None:
        name = prefix + get_uuid()
    validate_name(name, "network")

    try:
        net = restv2helper.get_network(bf_session, name)
        bf_session.network = str(net['name'])
        return bf_session.network
    except HTTPError as e:
        if e.response.status_code != 404:
            raise BatfishException('Unknown error accessing network', e)

    json_data = workhelper.get_data_init_network(bf_session, name)
    json_response = resthelper.get_json_response(
        bf_session, CoordConsts.SVC_RSC_INIT_NETWORK, json_data)

    network_name = json_response.get(CoordConsts.SVC_KEY_NETWORK_NAME)
    if network_name is None:
        raise BatfishException(
            "Network initialization failed. Server response: {}".format(
                json_response))

    bf_session.network = str(network_name)
    return bf_session.network
Beispiel #12
0
def _bf_answer_obj(question_str, parameters_str, question_name, background,
                   snapshot, reference_snapshot, extra_args):
    # type: (str, str, str, bool, str, Optional[str], Optional[Dict[str, Any]]) -> Union[Answer, str]
    from pybatfish.client.commands import bf_session

    json.loads(parameters_str)  # a syntactic check for parametersStr
    if not question_name:
        question_name = Options.default_question_prefix + "_" + get_uuid()

    # Upload the question
    json_data = workhelper.get_data_upload_question(bf_session, question_name,
                                                    question_str,
                                                    parameters_str)
    resthelper.get_json_response(bf_session,
                                 CoordConsts.SVC_RSC_UPLOAD_QUESTION,
                                 json_data)

    # Answer the question
    work_item = workhelper.get_workitem_answer(bf_session, question_name,
                                               snapshot, reference_snapshot)
    workhelper.execute(work_item, bf_session, background, extra_args)

    if background:
        return work_item.id

    # get the answer
    answer_bytes = resthelper.get_answer(bf_session, snapshot, question_name,
                                         reference_snapshot)

    # In Python 3.x, answer needs to be decoded before it can be used
    # for things like json.loads (<= 3.6).
    if six.PY3:
        answer_string = answer_bytes.decode(encoding="utf-8")
    else:
        answer_string = answer_bytes
    answer_obj = json.loads(answer_string)

    return answer.from_string(answer_obj[1]['answer'])
Beispiel #13
0
 def __init__(self, session):
     # type: (Session) -> None
     self.id = batfishutils.get_uuid()  # type: str
     self.network = session.network  # type: Optional[str]
     self.requestParams = dict(session.additional_args)  # type: Dict
Beispiel #14
0
def bf_init_snapshot(upload, name=None, overwrite=False, background=False):
    # type: (str, Optional[str], bool, bool) -> Union[str, Dict[str, str]]
    """Initialize a new snapshot.

    :param upload: snapshot to upload
    :type upload: zip file or directory
    :param name: name of the snapshot to initialize
    :type name: string
    :param overwrite: whether or not to overwrite an existing snapshot with the
       same name
    :type overwrite: bool
    :param background: whether or not to run the task in the background
    :type background: bool
    :return: name of initialized snapshot, or JSON dictionary of task status if background=True
    :rtype: Union[str, Dict]
    """
    if bf_session.network is None:
        bf_set_network()

    if name is None:
        name = Options.default_snapshot_prefix + get_uuid()
    validate_name(name)

    if name in bf_list_snapshots():
        if overwrite:
            bf_delete_snapshot(name)
        else:
            raise ValueError('A snapshot named '
                             '{}'
                             ' already exists in network '
                             '{}'
                             ''.format(name, bf_session.network))

    file_to_send = upload
    if os.path.isdir(upload):
        temp_zip_file = tempfile.NamedTemporaryFile()
        zip_dir(upload, temp_zip_file)
        file_to_send = temp_zip_file.name

    json_data = workhelper.get_data_upload_snapshot(bf_session, name,
                                                    file_to_send)
    resthelper.get_json_response(bf_session,
                                 CoordConsts.SVC_RSC_UPLOAD_SNAPSHOT,
                                 json_data)

    work_item = workhelper.get_workitem_parse(bf_session, name)
    answer_dict = workhelper.execute(work_item,
                                     bf_session,
                                     background=background)
    if background:
        bf_session.baseSnapshot = name
        return answer_dict

    status = WorkStatusCode(answer_dict["status"])
    if status != WorkStatusCode.TERMINATEDNORMALLY:
        raise BatfishException(
            'Initializing snapshot {ss} failed with status {status}'.format(
                ss=name, status=status))
    else:
        bf_session.baseSnapshot = name
        bf_logger.info("Default snapshot is now set to %s",
                       bf_session.baseSnapshot)
        return bf_session.baseSnapshot
Beispiel #15
0
def bf_fork_snapshot(base_name,
                     name=None,
                     overwrite=False,
                     background=False,
                     deactivate_interfaces=None,
                     deactivate_links=None,
                     deactivate_nodes=None,
                     restore_interfaces=None,
                     restore_links=None,
                     restore_nodes=None,
                     add_files=None):
    # type: (str, Optional[str], bool, bool, Optional[List[Interface]], Optional[List[Edge]], Optional[List[str]], Optional[List[Interface]], Optional[List[Edge]], Optional[List[str]], Optional[str]) -> Union[str, Dict, None]
    """Copy an existing snapshot and deactivate or reactivate specified interfaces, nodes, and links on the copy.

    :param base_name: name of the snapshot to copy
    :type base_name: string
    :param name: name of the snapshot to initialize
    :type name: string
    :param overwrite: whether or not to overwrite an existing snapshot with the
        same name
    :type overwrite: bool
    :param background: whether or not to run the task in the background
    :type background: bool
    :param deactivate_interfaces: list of interfaces to deactivate in new snapshot
    :type deactivate_interfaces: list[Interface]
    :param deactivate_links: list of links to deactivate in new snapshot
    :type deactivate_links: list[Edge]
    :param deactivate_nodes: list of names of nodes to deactivate in new snapshot
    :type deactivate_nodes: list[str]
    :param restore_interfaces: list of interfaces to reactivate
    :type restore_interfaces: list[Interface]
    :param restore_links: list of links to reactivate
    :type restore_links: list[Edge]
    :param restore_nodes: list of names of nodes to reactivate
    :type restore_nodes: list[str]
    :param add_files: path to zip file or directory containing files to add
    :type add_files: str
    :return: name of initialized snapshot, JSON dictionary of task status if
        background=True, or None if the call fails
    :rtype: Union[str, Dict, None]
    """
    if bf_session.network is None:
        raise ValueError('Network must be set to fork a snapshot.')

    if name is None:
        name = Options.default_snapshot_prefix + get_uuid()
    validate_name(name)

    if name in bf_list_snapshots():
        if overwrite:
            bf_delete_snapshot(name)
        else:
            raise ValueError('A snapshot named '
                             '{}'
                             ' already exists in network '
                             '{}'
                             ''.format(name, bf_session.network))

    encoded_file = None
    if add_files is not None:
        file_to_send = add_files
        if os.path.isdir(add_files):
            temp_zip_file = tempfile.NamedTemporaryFile()
            zip_dir(add_files, temp_zip_file)
            file_to_send = temp_zip_file.name

        if os.path.isfile(file_to_send):
            with open(file_to_send, "rb") as f:
                encoded_file = base64.b64encode(f.read()).decode('ascii')

    json_data = {
        "snapshotBase": base_name,
        "snapshotNew": name,
        "deactivateInterfaces": deactivate_interfaces,
        "deactivateLinks": deactivate_links,
        "deactivateNodes": deactivate_nodes,
        "restoreInterfaces": restore_interfaces,
        "restoreLinks": restore_links,
        "restoreNodes": restore_nodes,
        "zipFile": encoded_file
    }
    restv2helper.fork_snapshot(bf_session, json_data)

    work_item = workhelper.get_workitem_parse(bf_session, name)
    answer_dict = workhelper.execute(work_item,
                                     bf_session,
                                     background=background)
    if background:
        bf_session.baseSnapshot = name
        return answer_dict

    status = WorkStatusCode(answer_dict['status'])
    if status != WorkStatusCode.TERMINATEDNORMALLY:
        raise BatfishException(
            'Forking snapshot {ss} from {base} failed with status {status}'.
            format(ss=name, base=base_name, status=status))
    else:
        bf_session.baseSnapshot = name
        bf_logger.info("Default snapshot is now set to %s",
                       bf_session.baseSnapshot)
        return bf_session.baseSnapshot
Beispiel #16
0
def bf_fork_snapshot(base_name,
                     name=None,
                     overwrite=False,
                     background=False,
                     deactivate_interfaces=None,
                     deactivate_links=None,
                     deactivate_nodes=None,
                     restore_interfaces=None,
                     restore_links=None,
                     restore_nodes=None,
                     add_files=None,
                     extra_args=None):
    # type: (str, Optional[str], bool, bool, Optional[List[Interface]], Optional[List[Edge]], Optional[List[str]], Optional[List[Interface]], Optional[List[Edge]], Optional[List[str]], Optional[str], Optional[Dict[str, Any]]) -> Union[str, Dict, None]
    """Copy an existing snapshot and deactivate or reactivate specified interfaces, nodes, and links on the copy.

    :param base_name: name of the snapshot to copy
    :type base_name: string
    :param name: name of the snapshot to initialize
    :type name: string
    :param overwrite: whether or not to overwrite an existing snapshot with the
        same name
    :type overwrite: bool
    :param background: whether or not to run the task in the background
    :type background: bool
    :param deactivate_interfaces: list of interfaces to deactivate in new snapshot
    :type deactivate_interfaces: list[Interface]
    :param deactivate_links: list of links to deactivate in new snapshot
    :type deactivate_links: list[Edge]
    :param deactivate_nodes: list of names of nodes to deactivate in new snapshot
    :type deactivate_nodes: list[str]
    :param restore_interfaces: list of interfaces to reactivate
    :type restore_interfaces: list[Interface]
    :param restore_links: list of links to reactivate
    :type restore_links: list[Edge]
    :param restore_nodes: list of names of nodes to reactivate
    :type restore_nodes: list[str]
    :param add_files: path to zip file or directory containing files to add
    :type add_files: str
    :param extra_args: extra arguments to be passed to the parse command. See bf_session.additionalArgs.
    :type extra_args: dict
    :return: name of initialized snapshot, JSON dictionary of task status if
        background=True, or None if the call fails
    :rtype: Union[str, Dict, None]
    """
    if bf_session.network is None:
        raise ValueError('Network must be set to fork a snapshot.')

    if name is None:
        name = Options.default_snapshot_prefix + get_uuid()
    validate_name(name)

    if name in bf_list_snapshots():
        if overwrite:
            bf_delete_snapshot(name)
        else:
            raise ValueError('A snapshot named '
                             '{}'
                             ' already exists in network '
                             '{}'
                             ''.format(name, bf_session.network))

    encoded_file = None
    if add_files is not None:
        file_to_send = add_files
        if os.path.isdir(add_files):
            temp_zip_file = tempfile.NamedTemporaryFile()
            zip_dir(add_files, temp_zip_file)
            file_to_send = temp_zip_file.name

        if os.path.isfile(file_to_send):
            with open(file_to_send, "rb") as f:
                encoded_file = base64.b64encode(f.read()).decode('ascii')

    json_data = {
        "snapshotBase": base_name,
        "snapshotNew": name,
        "deactivateInterfaces": deactivate_interfaces,
        "deactivateLinks": deactivate_links,
        "deactivateNodes": deactivate_nodes,
        "restoreInterfaces": restore_interfaces,
        "restoreLinks": restore_links,
        "restoreNodes": restore_nodes,
        "zipFile": encoded_file
    }
    restv2helper.fork_snapshot(bf_session, json_data)

    return _parse_snapshot(name, background, extra_args)