Migrate to Python 3.6+ (#70)

* Migrate to Python 3.6+

Drop support of Python < 3.6
- remove six + future deps + usage
- update deps
- remove encoding
- remove (object)

* Fix pylint

- pip3 super() style
- remove simplejson dep + usage

* Black format

* lint setup

* Travix CI py version

* Use f string

* Basic Typing

* Revert setup version bump
This commit is contained in:
Quentame
2020-10-15 00:48:35 +02:00
committed by GitHub
parent ce8c2c4e64
commit 212f029478
38 changed files with 165 additions and 163 deletions

View File

@ -3,8 +3,8 @@ dist: xenial
language: python
python:
- 2.7
- 3.4
- 3.6
- 3.7
- 3.8
cache:
pip: true
@ -17,7 +17,7 @@ install:
- python setup.py sdist
before_script:
- pylint synology_dsm tests
- ./scripts/check_format.sh
- black --check --fast .
script:
- py.test

View File

@ -1,5 +1,3 @@
requests>=2.20.0
urllib3>=1.24.3,<1.25
six>=1.14.0
future>=0.18.2
simplejson>=3.16.0
requests>=2.24.0
# Constrain urllib3 to ensure we deal with CVE-2019-11236 & CVE-2019-11324
urllib3>=1.24.3

View File

@ -1,3 +1,4 @@
pytest
pylint>=1.9.5,<=2.4.4
pylint>=2.6.0
pylint-strict-informational==0.1
black==20.8b1

View File

@ -1,16 +0,0 @@
./scripts/common.sh
if ! hash python3; then
echo "python3 is not installed"
exit 0
fi
ver=$(python3 -V 2>&1 | sed 's/.* \([0-9]\).\([0-9]\).*/\1\2/')
if [ "$ver" -lt "36" ]; then
echo "This script requires python 3.6 or greater"
exit 0
fi
pip install black==19.10b0
black --check --fast .

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Synology DSM setup."""
# NOTE(ProtoThis) Guidelines for Major.Minor.Micro
# - Major means an API contract change
@ -7,7 +7,6 @@
# - Micro means change of any kind (unless significant enough for a minor/major).
from setuptools import setup, find_packages
from codecs import open
REPO_URL = "https://github.com/ProtoThis/python-synology"
VERSION = "0.9.0"
@ -25,23 +24,24 @@ setup(
download_url=REPO_URL + "/tarball/" + VERSION,
description="Python API for communication with Synology DSM",
long_description=long_description,
author="FG van Zeelst (ProtoThis)",
author="Quentin POLLET (Quentame) & FG van Zeelst (ProtoThis)",
packages=find_packages(include=["synology_dsm*"]),
install_requires=required,
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
python_requires=">=3.6",
license="MIT",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Software Development :: Libraries",
],
keywords=["synology-dsm", "synology"],
)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""DSM Security data."""
class SynoCoreSecurity(object):
class SynoCoreSecurity:
"""Class containing Security data."""
API_KEY = "SYNO.Core.SecurityScan.Status"

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
"""Shared Folders data."""
from synology_dsm.helpers import SynoFormatHelper
class SynoCoreShare(object):
class SynoCoreShare:
"""Class containing Share data."""
API_KEY = "SYNO.Core.Share"

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
"""DSM Utilization data."""
from synology_dsm.helpers import SynoFormatHelper
class SynoCoreUtilization(object):
class SynoCoreUtilization:
"""Class containing Utilization data."""
API_KEY = "SYNO.Core.System.Utilization"

View File

@ -2,7 +2,7 @@
from .task import SynoDownloadTask
class SynoDownloadStation(object):
class SynoDownloadStation:
"""An implementation of a Synology DownloadStation."""
API_KEY = "SYNO.DownloadStation.*"

View File

@ -1,7 +1,7 @@
"""DownloadStation task."""
class SynoDownloadTask(object):
class SynoDownloadTask:
"""An representation of a Synology DownloadStation task."""
def __init__(self, data):

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""DSM Information data."""
class SynoDSMInformation(object):
class SynoDSMInformation:
"""Class containing Information data."""
API_KEY = "SYNO.DSM.Info"

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""DSM Network data."""
class SynoDSMNetwork(object):
class SynoDSMNetwork:
"""Class containing Network data."""
API_KEY = "SYNO.DSM.Network"

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
"""DSM Storage data."""
from __future__ import division
from synology_dsm.helpers import SynoFormatHelper
class SynoStorage(object):
class SynoStorage:
"""Class containing Storage data."""
API_KEY = "SYNO.Storage.CGI.Storage"

View File

@ -5,7 +5,7 @@ from .camera import SynoCamera
from .const import MOTION_DETECTION_BY_SURVEILLANCE, MOTION_DETECTION_DISABLED
class SynoSurveillanceStation(object):
class SynoSurveillanceStation:
"""An implementation of a Synology SurveillanceStation."""
API_KEY = "SYNO.SurveillanceStation.*"
@ -37,12 +37,12 @@ class SynoSurveillanceStation(object):
)["data"]
)
live_view_data = self._dsm.get(
live_view_datas = self._dsm.get(
self.CAMERA_API_KEY,
"GetLiveViewPath",
{"idList": ",".join(str(k) for k in self._cameras_by_id)},
)["data"]
for live_view_data in live_view_data:
for live_view_data in live_view_datas:
self._cameras_by_id[live_view_data["id"]].live_view.update(live_view_data)
# Global

View File

@ -2,7 +2,7 @@
from .const import RECORDING_STATUS, MOTION_DETECTION_DISABLED
class SynoCamera(object):
class SynoCamera:
"""An representation of a Synology SurveillanceStation camera."""
def __init__(self, data, live_view_data=None):
@ -62,7 +62,7 @@ class SynoCamera(object):
return self._data["recStatus"] in RECORDING_STATUS
class SynoCameraLiveView(object):
class SynoCameraLiveView:
"""An representation of a Synology SurveillanceStation camera live view."""
def __init__(self, data):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Library constants."""
# APIs

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Library exceptions."""
from .const import API_AUTH, ERROR_AUTH, ERROR_COMMON, ERROR_DOWNLOAD_SEARCH, ERROR_DOWNLOAD_TASK, ERROR_FILE, ERROR_SURVEILLANCE, ERROR_VIRTUALIZATION
@ -24,7 +23,7 @@ class SynologyDSMException(Exception):
reason = "Unknown"
error_message={"api": api, "code": code, "reason": reason, "details": details}
super(SynologyDSMException, self).__init__(error_message)
super().__init__(error_message)
# Request
class SynologyDSMRequestException(SynologyDSMException):
@ -34,57 +33,56 @@ class SynologyDSMRequestException(SynologyDSMException):
ex_reason = exception.args[0]
if hasattr(exception.args[0], "reason"):
ex_reason = exception.args[0].reason
message = "%s = %s" % (ex_class, ex_reason)
super(SynologyDSMRequestException, self).__init__(None, -1, message)
super().__init__(None, -1, f"{ex_class} = {ex_reason}")
# API
class SynologyDSMAPINotExistsException(SynologyDSMException):
"""API not exists exception."""
def __init__(self, api):
super(SynologyDSMAPINotExistsException, self).__init__(api, -2, "API %s does not exists" % api)
super().__init__(api, -2, f"API {api} does not exists")
class SynologyDSMAPIErrorException(SynologyDSMException):
"""API returns an error exception."""
def __init__(self, api, code, details):
super(SynologyDSMAPIErrorException, self).__init__(api, code, details)
super().__init__(api, code, details)
# Login
class SynologyDSMLoginFailedException(SynologyDSMException):
"""Failed to login exception."""
def __init__(self, code, details=None):
super(SynologyDSMLoginFailedException, self).__init__(API_AUTH, code, details)
super().__init__(API_AUTH, code, details)
class SynologyDSMLoginInvalidException(SynologyDSMLoginFailedException):
"""Invalid password & not admin account exception."""
def __init__(self, username):
message = "Invalid password or not admin account: %s" % username
super(SynologyDSMLoginInvalidException, self).__init__(400, message)
message = f"Invalid password or not admin account: {username}"
super().__init__(400, message)
class SynologyDSMLoginDisabledAccountException(SynologyDSMLoginFailedException):
"""Guest & disabled account exception."""
def __init__(self, username):
message = "Guest or disabled account: %s" % username
super(SynologyDSMLoginDisabledAccountException, self).__init__(401, message)
message = f"Guest or disabled account: {username}"
super().__init__(401, message)
class SynologyDSMLoginPermissionDeniedException(SynologyDSMLoginFailedException):
"""No access to login exception."""
def __init__(self, username):
message = "Permission denied for account: %s" % username
super(SynologyDSMLoginPermissionDeniedException, self).__init__(402, message)
message = f"Permission denied for account: {username}"
super().__init__(402, message)
class SynologyDSMLogin2SARequiredException(SynologyDSMLoginFailedException):
"""2SA required to login exception."""
def __init__(self, username):
message = "Two-step authentication required for account: %s" % username
super(SynologyDSMLogin2SARequiredException, self).__init__(403, message)
message = f"Two-step authentication required for account: {username}"
super().__init__(403, message)
class SynologyDSMLogin2SAFailedException(SynologyDSMLoginFailedException):
"""2SA code failed exception."""
def __init__(self):
message = "Two-step authentication failed, retry with a new pass code"
super(SynologyDSMLogin2SAFailedException, self).__init__(404, message)
super().__init__(404, message)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""Helpers."""
class SynoFormatHelper(object):
class SynoFormatHelper:
"""Class containing various formatting functions."""
@staticmethod

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
"""Class to interact with Synology DSM."""
from json import JSONDecodeError
from urllib.parse import quote
import socket
import urllib3
import six
from requests import Session
from requests.exceptions import RequestException
from simplejson.errors import JSONDecodeError
from .exceptions import (
SynologyDSMAPIErrorException,
@ -29,13 +29,8 @@ from .api.storage.storage import SynoStorage
from .api.surveillance_station import SynoSurveillanceStation
from .const import API_AUTH, API_INFO
if six.PY2:
from future.moves.urllib.parse import quote
else:
from urllib.parse import quote # pylint: disable=import-error,no-name-in-module
class SynologyDSM(object):
class SynologyDSM:
"""Class containing the main Synology DSM functions."""
DSM_5_WEIRD_URL_API = [
@ -44,14 +39,14 @@ class SynologyDSM(object):
def __init__(
self,
dsm_ip,
dsm_port,
username,
password,
use_https=False,
timeout=None,
device_token=None,
debugmode=False,
dsm_ip: str,
dsm_port: int,
username: str,
password: str,
use_https: bool = False,
timeout: int = None,
device_token: str = None,
debugmode: bool = False,
):
self.username = username
self._password = password
@ -86,16 +81,16 @@ class SynologyDSM(object):
# disable SSL warnings due to the auto-genenerated cert
urllib3.disable_warnings()
self._base_url = "https://%s:%s" % (dsm_ip, dsm_port)
self._base_url = f"https://{dsm_ip}:{dsm_port}"
else:
self._base_url = "http://%s:%s" % (dsm_ip, dsm_port)
self._base_url = f"http://{dsm_ip}:{dsm_port}"
def _debuglog(self, message):
def _debuglog(self, message: str):
"""Outputs message if debug mode is enabled."""
if self._debugmode:
print("DEBUG: " + message)
def _is_weird_api_url(self, api):
def _is_weird_api_url(self, api: str) -> bool:
"""Returns True if the API URL is not common (nas_base_url/webapi/path?params) [Only handles DSM 5 for now]."""
return (
api in self.DSM_5_WEIRD_URL_API
@ -104,15 +99,12 @@ class SynologyDSM(object):
and int(self._information.version) < 7321 # < DSM 6
)
def _build_url(self, api):
def _build_url(self, api: str) -> str:
if self._is_weird_api_url(api):
if api == SynoStorage.API_KEY:
return (
"%s/webman/modules/StorageManager/storagehandler.cgi?"
% self._base_url
)
return f"{self._base_url}/webman/modules/StorageManager/storagehandler.cgi?"
return "%s/webapi/%s?" % (self._base_url, self.apis[api]["path"])
return f"{self._base_url}/webapi/{self.apis[api]['path']}?"
def discover_apis(self):
"""Retreives available API infos from the NAS."""
@ -125,7 +117,7 @@ class SynologyDSM(object):
"""Gets available API infos from the NAS."""
return self._apis
def login(self, otp_code=None):
def login(self, otp_code: str = None):
"""Create a logged session."""
# First reset the session
self._debuglog("Creating new session")
@ -180,20 +172,26 @@ class SynologyDSM(object):
return True
@property
def device_token(self):
def device_token(self) -> str:
"""Gets the device token to remember the 2SA access was granted on this device."""
return self._device_token
def get(self, api, method, params=None, **kwargs):
def get(self, api: str, method: str, params: dict = None, **kwargs):
"""Handles API GET request."""
return self._request("GET", api, method, params, **kwargs)
def post(self, api, method, params=None, **kwargs):
def post(self, api: str, method: str, params: dict = None, **kwargs):
"""Handles API POST request."""
return self._request("POST", api, method, params, **kwargs)
def _request(
self, request_method, api, method, params=None, retry_once=True, **kwargs
self,
request_method: str,
api: str,
method: str,
params: dict = None,
retry_once: bool = True,
**kwargs
):
"""Handles API request."""
# Discover existing APIs
@ -252,17 +250,13 @@ class SynologyDSM(object):
return response
def _execute_request(self, method, url, params, **kwargs):
def _execute_request(self, method: str, url: str, params: dict, **kwargs):
"""Function to execute and handle a request."""
# Execute Request
try:
if method == "GET":
if six.PY2:
items = params.iteritems()
else:
items = params.items()
encoded_params = "&".join(
"%s=%s" % (key, quote(str(value))) for key, value in items
f"{key}={quote(str(value))}" for key, value in params.items()
)
response = self._session.get(
url, params=encoded_params, timeout=self._timeout, **kwargs
@ -300,9 +294,9 @@ class SynologyDSM(object):
raise RequestException(response)
except (RequestException, JSONDecodeError) as exp:
raise SynologyDSMRequestException(exp)
raise SynologyDSMRequestException(exp) from exp
def update(self, with_information=False, with_network=False):
def update(self, with_information: bool = False, with_network: bool = False):
"""Updates the various instanced modules."""
if self._download:
self._download.update()
@ -328,7 +322,7 @@ class SynologyDSM(object):
if self._surveillance:
self._surveillance.update()
def reset(self, api):
def reset(self, api: any) -> bool:
"""Reset an API to avoid fetching in on update."""
if isinstance(api, str):
if api in ("information", SynoDSMInformation.API_KEY):
@ -376,56 +370,56 @@ class SynologyDSM(object):
return False
@property
def download_station(self):
def download_station(self) -> SynoDownloadStation:
"""Gets NAS DownloadStation."""
if not self._download:
self._download = SynoDownloadStation(self)
return self._download
@property
def information(self):
def information(self) -> SynoDSMInformation:
"""Gets NAS informations."""
if not self._information:
self._information = SynoDSMInformation(self)
return self._information
@property
def network(self):
def network(self) -> SynoDSMNetwork:
"""Gets NAS network informations."""
if not self._network:
self._network = SynoDSMNetwork(self)
return self._network
@property
def security(self):
def security(self) -> SynoCoreSecurity:
"""Gets NAS security informations."""
if not self._security:
self._security = SynoCoreSecurity(self)
return self._security
@property
def utilisation(self):
def utilisation(self) -> SynoCoreUtilization:
"""Gets NAS utilisation informations."""
if not self._utilisation:
self._utilisation = SynoCoreUtilization(self)
return self._utilisation
@property
def storage(self):
def storage(self) -> SynoStorage:
"""Gets NAS storage informations."""
if not self._storage:
self._storage = SynoStorage(self)
return self._storage
@property
def share(self):
def share(self) -> SynoCoreShare:
"""Gets NAS shares information."""
if not self._share:
self._share = SynoCoreShare(self)
return self._share
@property
def surveillance_station(self):
def surveillance_station(self) -> SynoSurveillanceStation:
"""Gets NAS SurveillanceStation."""
if not self._surveillance:
self._surveillance = SynoSurveillanceStation(self)

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
"""Library tests."""
import six
from json import JSONDecodeError
from urllib.parse import urlencode
from requests.exceptions import ConnectionError as ConnError, RequestException, SSLError
from simplejson.errors import JSONDecodeError
from synology_dsm import SynologyDSM
from synology_dsm.exceptions import SynologyDSMRequestException
@ -71,7 +71,9 @@ API_SWITCHER = {
"DSM_INFORMATION": DSM_5_DSM_INFORMATION,
"DSM_NETWORK": DSM_5_DSM_NETWORK,
"CORE_UTILIZATION": DSM_5_CORE_UTILIZATION,
"STORAGE_STORAGE": {"RAID": DSM_5_STORAGE_STORAGE_DS410J_RAID5_4DISKS_1VOL,},
"STORAGE_STORAGE": {
"RAID": DSM_5_STORAGE_STORAGE_DS410J_RAID5_4DISKS_1VOL,
},
},
6: {
"API_INFO": DSM_6_API_INFO,
@ -93,11 +95,6 @@ API_SWITCHER = {
}
if six.PY2:
from future.moves.urllib.parse import urlencode
else:
from urllib.parse import urlencode # pylint: disable=import-error,no-name-in-module
VALID_HOST = "nas.mywebsite.me"
VALID_PORT = "443"
VALID_SSL = True
@ -161,7 +158,7 @@ class SynologyDSMMock(SynologyDSM):
if VALID_PORT not in url and "https" not in url:
raise SynologyDSMRequestException(
JSONDecodeError("Expecting value", "<html>document</html>", 0, None)
JSONDecodeError("Expecting value", "<html>document</html>", 0)
)
if VALID_PORT not in url:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 5 SYNO.Core.System.Utilization data."""
DSM_5_CORE_UTILIZATION = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 5 SYNO.DSM.Info data."""
DSM_5_DSM_INFORMATION_DS410J = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 5 SYNO.DSM.Network data."""
DSM_5_DSM_NETWORK = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 5 SYNO.Storage.CGI.Storage data."""
from tests.const import UNIQUE_KEY
@ -253,10 +252,18 @@ DSM_5_STORAGE_STORAGE_DS410J_RAID5_4DISKS_1VOL = {
"vol_path": "/volume1",
"vspace_can_do": {
"drbd": {
"resize": {"can_do": False, "errCode": 53504, "stopService": False,}
"resize": {
"can_do": False,
"errCode": 53504,
"stopService": False,
}
},
"flashcache": {
"apply": {"can_do": False, "errCode": 53504, "stopService": False,},
"apply": {
"can_do": False,
"errCode": 53504,
"stopService": False,
},
"remove": {
"can_do": False,
"errCode": 53504,

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.Core.SecurityScan.Status data."""
DSM_6_CORE_SECURITY = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.Core.Share data."""
DSM_6_CORE_SHARE = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.Core.System.Utilization data."""
DSM_6_CORE_UTILIZATION_ERROR_1055 = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.DownloadStation.Info data."""
DSM_6_DOWNLOAD_STATION_INFO_INFO = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.DownloadStation.Statistic data."""
DSM_6_DOWNLOAD_STATION_STAT_INFO = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.DownloadStation.Task data."""
DSM_6_DOWNLOAD_STATION_TASK_LIST = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.DSM.Info data."""
DSM_6_DSM_INFORMATION_DS213_PLUS = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.DSM.Network data."""
DSM_6_DSM_NETWORK = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.Storage.CGI.Storage data."""
from tests.const import UNIQUE_KEY
@ -1979,7 +1978,10 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
"env": {
"batchtask": {"max_task": 64, "remain_task": 64},
"bay_number": "4",
"data_scrubbing": {"sche_enabled": "0", "sche_status": "disabled",},
"data_scrubbing": {
"sche_enabled": "0",
"sche_status": "disabled",
},
"ebox": [],
"fs_acting": False,
"isSyncSysPartition": False,
@ -1993,8 +1995,15 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
"ram_size": 4,
"ram_size_required": 32,
"showpooltab": False,
"status": {"system_crashed": False, "system_need_repair": False,},
"support": {"ebox": True, "raid_cross": True, "sysdef": True,},
"status": {
"system_crashed": False,
"system_need_repair": False,
},
"support": {
"ebox": True,
"raid_cross": True,
"sysdef": True,
},
"support_fit_fs_limit": True,
"unique_key": UNIQUE_KEY,
"volume_full_critical": 0.1,
@ -2040,9 +2049,21 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
{
"designedDiskCount": 3,
"devices": [
{"id": "sdc", "slot": 2, "status": "normal",},
{"id": "sdb", "slot": 1, "status": "normal",},
{"id": "sda", "slot": 0, "status": "normal",},
{
"id": "sdc",
"slot": 2,
"status": "normal",
},
{
"id": "sdb",
"slot": 1,
"status": "normal",
},
{
"id": "sda",
"slot": 0,
"status": "normal",
},
],
"hasParity": True,
"minDevSize": "4000681164800",
@ -2053,7 +2074,10 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
}
],
"scrubbingStatus": "no_action",
"size": {"total": "7991698522112", "used": "7991698522112",},
"size": {
"total": "7991698522112",
"used": "7991698522112",
},
"space_path": "/dev/md2",
"ssd_trim": {"support": "not support"},
"status": "normal",
@ -2068,9 +2092,21 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
}
},
"flashcache": {
"apply": {"can_do": True, "errCode": 0, "stopService": True,},
"remove": {"can_do": True, "errCode": 0, "stopService": False,},
"resize": {"can_do": True, "errCode": 0, "stopService": False,},
"apply": {
"can_do": True,
"errCode": 0,
"stopService": True,
},
"remove": {
"can_do": True,
"errCode": 0,
"stopService": False,
},
"resize": {
"can_do": True,
"errCode": 0,
"stopService": False,
},
},
"snapshot": {
"resize": {
@ -2142,9 +2178,21 @@ DSM_6_STORAGE_STORAGE_DS918_PLUS_RAID5_3DISKS_1VOL = {
}
},
"flashcache": {
"apply": {"can_do": True, "errCode": 0, "stopService": True,},
"remove": {"can_do": True, "errCode": 0, "stopService": False,},
"resize": {"can_do": True, "errCode": 0, "stopService": False,},
"apply": {
"can_do": True,
"errCode": 0,
"stopService": True,
},
"remove": {
"can_do": True,
"errCode": 0,
"stopService": False,
},
"resize": {
"can_do": True,
"errCode": 0,
"stopService": False,
},
},
"snapshot": {
"resize": {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.SurveillanceStation.Camera data."""
DSM_6_SURVEILLANCE_STATION_CAMERA_LIST = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""DSM 6 SYNO.API.SurveillanceStation.HomeMode data."""
DSM_6_SURVEILLANCE_STATION_HOME_MODE_GET_INFO = {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Test constants."""
# API test data are localized in

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Synology DSM tests."""
from unittest import TestCase
import pytest

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Synology DSM tests."""
from unittest import TestCase