Esempio n. 1
0
    def on_event(self, event, payload):
        if event == "plugin_backup_backup_created":
            # Helper function for human readable sizes
            def _convert_size(size_bytes):
                if size_bytes == 0:
                    return "0B"
                size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB",
                             "YB")
                i = int(math.floor(math.log(size_bytes, 1024)))
                p = math.pow(1024, i)
                s = round(size_bytes / p, 2)
                return "%s %s" % (s, size_name[i])

            now = datetime.now()

            davoptions = {
                'webdav_hostname': self._settings.get(["server"]),
                'webdav_login': self._settings.get(["username"]),
                'webdav_password': self._settings.get(["password"]),
                'webdav_timeout': self._settings.get(["timeout"]),
            }

            backup_path = payload["path"]
            backup_name = payload["name"]
            self._logger.info("Backup " + backup_path +
                              " created, will now attempt to upload to " +
                              davoptions["webdav_hostname"])

            davclient = Client(davoptions)
            davclient.verify = self._settings.get(["verify_certificate"])
            check_space = self._settings.get(["check_space"])
            upload_path = now.strftime(self._settings.get(["upload_path"]))
            upload_path = ospath.join("/", upload_path)

            if self._settings.get(["upload_name"]):
                upload_name = now.strftime(self._settings.get(
                    ["upload_name"])) + ospath.splitext(backup_path)[1]
            else:
                upload_name = backup_name
            self._logger.debug("Filename for upload: " + upload_name)

            upload_file = ospath.join("/", upload_path, upload_name)
            upload_temp = ospath.join("/", upload_path, upload_name + ".tmp")

            self._logger.debug("Upload location: " + upload_file)

            # Check actual connection to the WebDAV server as the check command will not do this.
            if check_space:
                self._logger.debug("Attempting to check free space.")
                try:
                    # If the resource was not found
                    dav_free = davclient.free()
                    if dav_free < 0:
                        # If we get a negative free size, this server is not returning correct value.
                        check_space = False
                        self._logger.warning(
                            "Free space on server: " +
                            _convert_size(dav_free) +
                            ", it appears your server does not support reporting size correctly but it's still a proper way to check connectivity."
                        )
                    else:
                        self._logger.info("Free space on server: " +
                                          _convert_size(dav_free))
                except RemoteResourceNotFound as exception:
                    self._logger.error(
                        "Resource was not found, something is probably wrong with your settings."
                    )
                    return
                except ResponseErrorCode as exception:
                    # Write error and exit function
                    status = HTTPStatus(exception.code)
                    error_switcher = {
                        400: "Bad request",
                        401: "Unauthorized",
                        403: "Forbidden",
                        404: "Not found",
                        405: "Method not allowed",
                        408: "Request timeout",
                        500: "Internal error",
                        501: "Not implemented",
                        502: "Bad gateway",
                        503: "Service unavailable",
                        504: "Gateway timeout",
                        508: "Loop detected",
                    }
                    if (exception.code == 401):
                        http_error = "HTTP error 401 encountered, your credentials are most likely wrong."
                    else:
                        http_error = "HTTP error encountered: " + str(
                            status.value) + " " + error_switcher.get(
                                exception.code, status.phrase)
                    self._logger.error(http_error)
                    return
                except WebDavException as exception:
                    self._logger.error(
                        "An unexpected WebDAV error was encountered: " +
                        exception.args)
                    raise
            else:
                self._logger.debug(
                    "Not checking free space, just try to check the WebDAV root."
                )
                # Not as proper of a check as retrieving size, but it's something.
                if davclient.check("/"):
                    self._logger.debug("Server returned WebDAV root.")
                else:
                    self._logger.error(
                        "Server did not return WebDAV root, something is probably wronkg with your settings."
                    )
                    return

            backup_size = ospath.getsize(backup_path)
            self._logger.info("Backup file size: " +
                              _convert_size(backup_size))

            if check_space and (backup_size > dav_free):
                self._logger.error("Unable to upload, size is" +
                                   _convert_size(backup_size) +
                                   ", free space is " +
                                   _convert_size(dav_free))
                return
            else:
                # Helper function to recursively create paths
                def _recursive_create_path(path):
                    # Append leading / for preventing abspath issues
                    path = ospath.join("/", path)
                    if davclient.check(path):
                        self._logger.debug("Directory " + path + " was found.")
                        return True
                    else:
                        if path != "/":
                            self._logger.debug(
                                "Directory " + path +
                                " was not found, checking parent.")
                            if _recursive_create_path(
                                    ospath.abspath(ospath.join(path, ".."))):
                                davclient.mkdir(path)
                                self._logger.debug("Directory " + path +
                                                   " has been created.")
                                return True
                        else:
                            self._logger.error(
                                "Could not find WebDAV root, something is probably wrong with your settings."
                            )
                            return False

                if _recursive_create_path(upload_path):
                    self._logger.debug("Uploading " + backup_path + " to " +
                                       upload_temp)
                    davclient.upload_sync(remote_path=upload_temp,
                                          local_path=backup_path)
                    self._logger.debug("Moving " + upload_temp + " to " +
                                       upload_file)
                    davclient.move(remote_path_from=upload_temp,
                                   remote_path_to=upload_file)
                    self._logger.info(
                        "Backup has been uploaded successfully to " +
                        davoptions["webdav_hostname"] + " as " + upload_file)
                else:
                    self._logger.error(
                        "Something went wrong trying to check/create the upload path."
                    )
class ClientTestCase(TestCase):
    remote_path_file = 'test_dir/test.txt'
    remote_path_file2 = 'test_dir2/test.txt'
    remote_path_dir = 'test_dir'
    remote_path_dir2 = 'test_dir2'
    local_base_dir = 'tests/'
    local_file = 'test.txt'
    local_file_path = local_base_dir + 'test.txt'
    local_path_dir = local_base_dir + 'res/test_dir'

    def setUp(self):
        options = {
            'webdav_hostname': 'https://webdav.yandex.ru',
            'webdav_login': '******',
            'webdav_password': '******'
        }
        self.client = Client(options)
        if path.exists(path=self.local_path_dir):
            shutil.rmtree(path=self.local_path_dir)

    def tearDown(self):
        if path.exists(path=self.local_path_dir):
            shutil.rmtree(path=self.local_path_dir)
        if self.client.check(remote_path=self.remote_path_dir):
            self.client.clean(remote_path=self.remote_path_dir)
        if self.client.check(remote_path=self.remote_path_dir2):
            self.client.clean(remote_path=self.remote_path_dir2)

    def test_list(self):
        self._prepare_for_downloading()
        file_list = self.client.list()
        self.assertIsNotNone(file_list, 'List of files should not be None')
        self.assertGreater(file_list.__len__(), 0,
                           'Expected that amount of files more then 0')

    def test_free(self):
        self.assertGreater(
            self.client.free(), 0,
            'Expected that free space on WebDAV server is more then 0 bytes')

    def test_check(self):
        self.assertTrue(self.client.check(),
                        'Expected that root directory is exist')

    def test_mkdir(self):
        if self.client.check(remote_path=self.remote_path_dir):
            self.client.clean(remote_path=self.remote_path_dir)
        self.client.mkdir(remote_path=self.remote_path_dir)
        self.assertTrue(self.client.check(remote_path=self.remote_path_dir),
                        'Expected the directory is created.')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_to(self):
        self._prepare_for_downloading()
        buff = BytesIO()
        self.client.download_from(buff=buff, remote_path=self.remote_path_file)
        self.assertEquals(buff.getvalue(),
                          'test content for testing of webdav client')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download(self):
        self._prepare_for_downloading()
        self.client.download(local_path=self.local_path_dir,
                             remote_path=self.remote_path_dir)
        self.assertTrue(path.exists(self.local_path_dir),
                        'Expected the directory is downloaded.')
        self.assertTrue(path.isdir(self.local_path_dir),
                        'Expected this is a directory.')
        self.assertTrue(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file is downloaded')
        self.assertTrue(
            path.isfile(self.local_path_dir + os.path.sep +
                        self.local_path_file), 'Expected this is a file')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_sync(self):
        self._prepare_for_downloading()
        os.mkdir(self.local_path_dir)

        def callback():
            self.assertTrue(
                path.exists(self.local_path_dir + os.path.sep +
                            self.local_file),
                'Expected the file is downloaded')
            self.assertTrue(
                path.isfile(self.local_path_dir + os.path.sep +
                            self.local_file), 'Expected this is a file')

        self.client.download_sync(local_path=self.local_path_dir +
                                  os.path.sep + self.local_file,
                                  remote_path=self.remote_path_file,
                                  callback=callback)
        self.assertTrue(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file has already been downloaded')

    @unittest.skip(
        "Yandex brakes response for file it contains property resourcetype as collection but it should "
        "be empty for file")
    def test_download_async(self):
        self._prepare_for_downloading()
        os.mkdir(self.local_path_dir)

        def callback():
            self.assertTrue(
                path.exists(self.local_path_dir + os.path.sep +
                            self.local_file),
                'Expected the file is downloaded')
            self.assertTrue(
                path.isfile(self.local_path_dir + os.path.sep +
                            self.local_file), 'Expected this is a file')

        self.client.download_async(local_path=self.local_path_dir +
                                   os.path.sep + self.local_file,
                                   remote_path=self.remote_path_file,
                                   callback=callback)
        self.assertFalse(
            path.exists(self.local_path_dir + os.path.sep + self.local_file),
            'Expected the file has not been downloaded yet')

    def test_upload_from(self):
        self._prepare_for_uploading()
        buff = StringIO(u'test content for testing of webdav client')
        self.client.upload_to(buff=buff, remote_path=self.remote_path_file)
        self.assertTrue(self.client.check(self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload(self):
        self._prepare_for_uploading()
        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)
        self.assertTrue(self.client.check(self.remote_path_dir),
                        'Expected the directory is created.')
        self.assertTrue(self.client.check(self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload_file(self):
        self._prepare_for_uploading()
        self.client.upload_file(remote_path=self.remote_path_file,
                                local_path=self.local_file_path)
        self.assertTrue(self.client.check(remote_path=self.remote_path_file),
                        'Expected the file is uploaded.')

    def test_upload_sync(self):
        self._prepare_for_uploading()

        def callback():
            self.assertTrue(self.client.check(self.remote_path_dir),
                            'Expected the directory is created.')
            self.assertTrue(self.client.check(self.remote_path_file),
                            'Expected the file is uploaded.')

        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)

    def test_upload_async(self):
        self._prepare_for_uploading()

        def callback():
            self.assertTrue(self.client.check(self.remote_path_dir),
                            'Expected the directory is created.')
            self.assertTrue(self.client.check(self.remote_path_file),
                            'Expected the file is uploaded.')

        self.client.upload(remote_path=self.remote_path_file,
                           local_path=self.local_path_dir)

    def test_copy(self):
        self._prepare_for_downloading()
        self.client.mkdir(remote_path=self.remote_path_dir2)
        self.client.copy(remote_path_from=self.remote_path_file,
                         remote_path_to=self.remote_path_file2)
        self.assertTrue(self.client.check(remote_path=self.remote_path_file2))

    def test_move(self):
        self._prepare_for_downloading()
        self.client.mkdir(remote_path=self.remote_path_dir2)
        self.client.move(remote_path_from=self.remote_path_file,
                         remote_path_to=self.remote_path_file2)
        self.assertFalse(self.client.check(remote_path=self.remote_path_file))
        self.assertTrue(self.client.check(remote_path=self.remote_path_file2))

    def test_clean(self):
        self._prepare_for_downloading()
        self.client.clean(remote_path=self.remote_path_dir)
        self.assertFalse(self.client.check(remote_path=self.remote_path_file))
        self.assertFalse(self.client.check(remote_path=self.remote_path_dir))

    def test_info(self):
        self._prepare_for_downloading()
        result = self.client.info(remote_path=self.remote_path_file)
        self.assertEquals(result['name'], 'test.txt')
        self.assertEquals(result['size'], '41')
        self.assertTrue('created' in result)
        self.assertTrue('modified' in result)

    def test_directory_is_dir(self):
        self._prepare_for_downloading()
        self.assertTrue(self.client.is_dir(self.remote_path_dir),
                        'Should return True for directory')

    def test_file_is_not_dir(self):
        self._prepare_for_downloading()
        self.assertFalse(self.client.is_dir(self.remote_path_file),
                         'Should return False for file')

    def test_get_property_of_non_exist(self):
        self._prepare_for_downloading()
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={'name': 'aProperty'})
        self.assertEquals(
            result, None, 'For not found property should return value as None')

    def test_set_property(self):
        self._prepare_for_downloading()
        self.client.set_property(remote_path=self.remote_path_file,
                                 option={
                                     'namespace': 'test',
                                     'name': 'aProperty',
                                     'value': 'aValue'
                                 })
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty'
                                          })
        self.assertEquals(result, 'aValue', 'Property value should be set')

    def test_set_property_batch(self):
        self._prepare_for_downloading()
        self.client.set_property_batch(remote_path=self.remote_path_file,
                                       option=[{
                                           'namespace': 'test',
                                           'name': 'aProperty',
                                           'value': 'aValue'
                                       }, {
                                           'namespace': 'test',
                                           'name': 'aProperty2',
                                           'value': 'aValue2'
                                       }])
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty'
                                          })
        self.assertEquals(result, 'aValue',
                          'First property value should be set')
        result = self.client.get_property(remote_path=self.remote_path_file,
                                          option={
                                              'namespace': 'test',
                                              'name': 'aProperty2'
                                          })
        self.assertEquals(result, 'aValue2',
                          'Second property value should be set')

    def _prepare_for_downloading(self):
        if not self.client.check(remote_path=self.remote_path_dir):
            self.client.mkdir(remote_path=self.remote_path_dir)
        if not self.client.check(remote_path=self.remote_path_file):
            self.client.upload_file(remote_path=self.remote_path_file,
                                    local_path=self.local_file_path)
        if not path.exists(self.local_path_dir):
            os.makedirs(self.local_path_dir)

    def _prepare_for_uploading(self):
        if not self.client.check(remote_path=self.remote_path_dir):
            self.client.mkdir(remote_path=self.remote_path_dir)
        if not path.exists(path=self.local_path_dir):
            os.makedirs(self.local_path_dir)
        if not path.exists(path=self.local_path_dir + os.sep +
                           self.local_file):
            shutil.copy(src=self.local_file_path,
                        dst=self.local_path_dir + os.sep + self.local_file)