<commit_before>'''
Implements the targetcli root UI.

This file is part of targetcli.
Copyright (c) 2011-2013 by Datera, Inc

Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
'''

from datetime import datetime
from glob import glob
import os
import re
import shutil
import stat
import filecmp
import gzip

from configshell_fb import ExecutionError
from rtslib_fb import RTSRoot
from rtslib_fb.utils import ignored

from .ui_backstore import complete_path, UIBackstores
from .ui_node import UINode
from .ui_target import UIFabricModule

default_save_file = "/etc/target/saveconfig.json"
universal_prefs_file = "/etc/target/targetcli.conf"

class UIRoot(UINode):
    '''
    The targetcli hierarchy root node.
    '''
    def __init__(self, shell, as_root=False):
        UINode.__init__(self, '/', shell=shell)
        self.as_root = as_root
        self.rtsroot = RTSRoot()

    def refresh(self):
        '''
        Refreshes the tree of target fabric modules.
        '''
        self._children = set([])

        # Invalidate any rtslib caches
        if 'invalidate_caches' in dir(RTSRoot):
            self.rtsroot.invalidate_caches()

        UIBackstores(self)

        # only show fabrics present in the system
        for fm in self.rtsroot.fabric_modules:
            if fm.wwns == None or any(fm.wwns):
                UIFabricModule(fm, self)

    def _compare_files(self, backupfile, savefile):
        '''
        Compare backfile and saveconfig file
        '''
        if (os.path.splitext(backupfile)[1] == '.gz'):
            try:
                with gzip.open(backupfile, 'rb') as fbkp:
                    fdata_bkp = fbkp.read()
            except IOError as e:
                self.shell.log.warning("Could not gzip open backupfile %s: %s"
                                       % (backupfile, e.strerror))

        else:
            try:
                with open(backupfile, 'rb') as fbkp:
                    fdata_bkp = fbkp.read()
            except IOError as e:
                self.shell.log.warning("Could not open backupfile %s: %s"
                                       % (backupfile, e.strerror))

        try:
            with open(savefile, 'rb') as f:
                fdata = f.read()
        except IOError as e:
            self.shell.log.warning("Could not open saveconfig file %s: %s"
                                   % (savefile, e.strerror))

        if fdata_bkp == fdata:
            return True
        else:
            return False

    def _save_backups(self, savefile):
        '''
        Take backup of config-file if needed.
        '''
        # Only save backups if saving to default location
        if savefile != default_save_file:
            return

        backup_dir = os.path.dirname(savefile) + "/backup/"
        backup_name = "saveconfig-" + \
                      datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz"
        backupfile = backup_dir + backup_name
        backup_error = None

        if not os.path.exists(backup_dir):
            try:
                os.makedirs(backup_dir);
            except OSError as exe:
                raise ExecutionError("Cannot create backup directory [%s] %s."
                                     % (backup_dir, exe.strerror))

        # Only save backups if savefile exits
        if not os.path.exists(savefile):
            return

        backed_files_list = sorted(glob(os.path.dirname(savefile) + \
                                   "/backup/saveconfig-*json*"))

        # Save backup if backup dir is empty, or savefile is differnt from recent backup copy
        if not backed_files_list or not self._compare_files(backed_files_list[-1], savefile):
            try:
                with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') as f_out:
                    shutil.copyfileobj(f_in, f_out)
                    f_out.flush()
            except IOError as ioe:
                backup_error = ioe.strerror or "Unknown error"

            if backup_error == None:
                # remove excess backups
                max_backup_files = int(self.shell.prefs['max_backup_files'])

                try:
                    with open(universal_prefs_file) as prefs:
                        backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)]
                        if max_backup_files < int(backups[0].split('=')[1].strip()):
                            max_backup_files = int(backups[0].split('=')[1].strip())<commit_msg>Remove the extra semicolon in _save_backups<commit_after>'''
Implements the targetcli root UI.

This file is part of targetcli.
Copyright (c) 2011-2013 by Datera, Inc

Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
'''

from datetime import datetime
from glob import glob
import os
import re
import shutil
import stat
import filecmp
import gzip

from configshell_fb import ExecutionError
from rtslib_fb import RTSRoot
from rtslib_fb.utils import ignored

from .ui_backstore import complete_path, UIBackstores
from .ui_node import UINode
from .ui_target import UIFabricModule

default_save_file = "/etc/target/saveconfig.json"
universal_prefs_file = "/etc/target/targetcli.conf"

class UIRoot(UINode):
    '''
    The targetcli hierarchy root node.
    '''
    def __init__(self, shell, as_root=False):
        UINode.__init__(self, '/', shell=shell)
        self.as_root = as_root
        self.rtsroot = RTSRoot()

    def refresh(self):
        '''
        Refreshes the tree of target fabric modules.
        '''
        self._children = set([])

        # Invalidate any rtslib caches
        if 'invalidate_caches' in dir(RTSRoot):
            self.rtsroot.invalidate_caches()

        UIBackstores(self)

        # only show fabrics present in the system
        for fm in self.rtsroot.fabric_modules:
            if fm.wwns == None or any(fm.wwns):
                UIFabricModule(fm, self)

    def _compare_files(self, backupfile, savefile):
        '''
        Compare backfile and saveconfig file
        '''
        if (os.path.splitext(backupfile)[1] == '.gz'):
            try:
                with gzip.open(backupfile, 'rb') as fbkp:
                    fdata_bkp = fbkp.read()
            except IOError as e:
                self.shell.log.warning("Could not gzip open backupfile %s: %s"
                                       % (backupfile, e.strerror))

        else:
            try:
                with open(backupfile, 'rb') as fbkp:
                    fdata_bkp = fbkp.read()
            except IOError as e:
                self.shell.log.warning("Could not open backupfile %s: %s"
                                       % (backupfile, e.strerror))

        try:
            with open(savefile, 'rb') as f:
                fdata = f.read()
        except IOError as e:
            self.shell.log.warning("Could not open saveconfig file %s: %s"
                                   % (savefile, e.strerror))

        if fdata_bkp == fdata:
            return True
        else:
            return False

    def _save_backups(self, savefile):
        '''
        Take backup of config-file if needed.
        '''
        # Only save backups if saving to default location
        if savefile != default_save_file:
            return

        backup_dir = os.path.dirname(savefile) + "/backup/"
        backup_name = "saveconfig-" + \
                      datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz"
        backupfile = backup_dir + backup_name
        backup_error = None

        if not os.path.exists(backup_dir):
            try:
                os.makedirs(backup_dir)
            except OSError as exe:
                raise ExecutionError("Cannot create backup directory [%s] %s."
                                     % (backup_dir, exe.strerror))

        # Only save backups if savefile exits
        if not os.path.exists(savefile):
            return

        backed_files_list = sorted(glob(os.path.dirname(savefile) + \
                                   "/backup/saveconfig-*json*"))

        # Save backup if backup dir is empty, or savefile is differnt from recent backup copy
        if not backed_files_list or not self._compare_files(backed_files_list[-1], savefile):
            try:
                with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') as f_out:
                    shutil.copyfileobj(f_in, f_out)
                    f_out.flush()
            except IOError as ioe:
                backup_error = ioe.strerror or "Unknown error"

            if backup_error == None:
                # remove excess backups
                max_backup_files = int(self.shell.prefs['max_backup_files'])

                try:
                    with open(universal_prefs_file) as prefs:
                        backups = [line for line in prefs.read().splitlines() if re.match('^max_backup_files\s*=', line)]
                        if max_backup_files < int(backups[0].split('=')[1].strip()):
                            max_backup_files = int(backups[0].split('=')[1].strip())
{'fraction_non_alphanumeric': 0.09063655441191167, 'fraction_numerical': 0.003597481762766064, 'mean_word_length': 1.9228971962616823, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6233921', 'n_tokens_mistral': 2930, 'n_tokens_neox': 2783, 'n_words': 842}