Squarebox

Before you start

In order to use this Azure Cloud archiving plugin you need:

CatDV Server 7.3 or later

CatDV Pegasus 12.0b15 or later

CatDV Plugin licence 'ARCAZ' (Rest API licence with multiple sessions)

Client set to calculate MD5 when importing files, ideally

Client -> Preferences -> Import

Check "Automatically calculate MD5 when importing files"

** This will make imports slower. If this is not done, the Azure SDK will calculate it's own MD5 each time a file is archived, which will slow down the archiving process.

To trigger Azure cloud file transfers from the Worker you need:

Azure Worker Plugin 1.4.11, included in this installation as AzureWorker1.4.11.catdv

In addition, to run the archive service standalone (outside the server):

CatDV Service Control Panel 1.2 or later

Installation

NEW

Copy the whole directory extracted from this zip to the plugins directory:

e.g. on unix systems: /usr/local/catdvServer/plugins

e.g. on Windows: C:\Program Files (x86)\Square Box\CatDV Server\plugins

Set server properties required by the plugin:

Open 'Server Config' from the control panel

Enter required properties into the CatDV Server tab, 'Other' field. See 'Plugin server properties' below and 'Running archive service standalone', if applicable.

Enter optional properties if desired. See 'Plugin server properties' below.

Restart the CatDV server from the control panel

Open the CatDV client (or log the client back in to the server)

Configure the parameters to access Azure Cloud:

In the client, run Tools->Manage Azure Cloud Archive Service, enter account name, account key and container name and click 'save and start'. If the values entered are valid and a connection to Azure Cloud can be established, the archive service will start up automatically.

The service status can be monitored on the management and job queue screens.

*NB - If you don't already have an Azure Cloud account or you don't know the access keys see 'Azure Cloud setup' for details of how to set them up.

If running the archive service standalone (configured via a server property in step two), then configure and start the archive service via the service control panel. See 'Running archive service standalone'.

Verify the service setup:

In the client, run Tools->Manage Azure Cloud Archive Service again. The service status should be 'Running (online)'. The status may be 'Running (offline)' if Azure Cloud is not currently accessible.

Install (or delete) the Azure worker plugin:

IF the Worker IS NOT installed: Delete the Azure worker plugin file with extension '.catdv' from the azure plugin directory (see step 1).

IF the Worker IS installed:

Move the Azure worker plugin file with extension '.catdv' FROM the azure plugin directory installed in step 1 TO the worker plugins directory:

e.g. on a Mac: /Library/Application Support/Square Box/Extensions

e.g. on Windows: %ProgramData%\Square Box\Extensions

e.g. on Linux: /usr/local/catdvWorker/extensions

If the worker is running, re-connect to the server (Edit Config / CatDV Server / Reconnect) so that the archive plugin fields are available.

Verify that the Azure worker actions are available. **It may be necessary to quit and re-open the worker to pick up the new worker actions.

UPDATE

Make a note of the current plugin version - from Azure_README.txt / Azure_ReleaseNotes.txt in the Azure plugin directory or from the server log at start up when the plugin is loaded

If running the archive service standalone, use the service control panel to stop the service.

Stop the CatDV server from the control panel

Copy the whole directory extracted from this zip to the plugins directory:

e.g. on unix systems: /usr/local/catdvServer/plugins

e.g. on Windows: C:\Program Files (x86)\Square Box\CatDV Server\plugins

Remove or move the directory / files for the prior plugin from the plugins directory.

Carry out any 'Upgrade' instructions for each plugin version listed in the Azure_ReleaseNotes.txt (from this zip) above the last installed version, working back from the oldest applicable version

Read through the 'Changes' for each plugin version listed in the Azure_ReleaseNotes.txt (from this zip) above the last installed version, to check whether any new config params have been added which could usefully be customised for this installation

Restart the CatDV server from the control panel

If running the archive service standalone (configured via a server property in step two), then update and start the archive service via the service control panel. See 'Running archive service standalone'.

Open the CatDV client (or log the client back in to the server)

Verify the service setup: In the client, run Tools->Manage Azure Cloud Archive Service again. The service status should be 'Running (online)'. The status may be 'Running (offline)' if Azure is not currently accessible.

Update (or delete) the Azure worker plugin by following the instructions from step 8 for a new installation, making sure to move / remove the .catdv file for the prior version of the plugin from the extensions directory.

Running archive service standalone

By default, the service that handles archive jobs runs inside the plugin. To run it as a standalone process,

ensure that the service mode property is set to Standalone in the server when installing the plugin (see step 2

of Installation):

catdv.azure_archive.service_mode = Standalone

The following is required to run the plugin's archive service on a separate machine from the CatDV server:

· CatDV Service Control Panel 1.1.1 or later

** NB The archive service connection must be configured in the client / web plugin UI before it can be started.

To configure and start the standalone archive service using the service control panel:

Install the CatDV Service Control Panel

Open the CatDV Service Control Panel

Click 'Service Config' to open the service configuration

Enter license details on the Licensing tab

Enter required and optional service properties on the Settings tab. See 'Plugin server properties' below.

On the Installation tab, note the install location ('Server Install Path')

On the file system, navigate to the install location and copy the plugin files into the plugins sub-directory.

Click 'Start' to start the archive service.

To update the standalone archive service:

* The service should have been stopped via the Service Control Panel 'Stop' prior to stopping the CatDV Server.

Open the CatDV Service Control Panel

Add / edit service properties on the Settings tab if applicable.

Go to the install location (see Installation tab) and replace the plugin files in the plugins sub-directory with those for the latest version of the plugin.

Click 'Start' to start the archive service.

Plugin server properties

When running the archive service "InPlugin", the following server properties must be set:

catdv.azure_archive.licence_code = <generated licence code for plugin>

When running the archive service standalone, the following server properties must ALSO be set:

catdv.azure_archive.service_mode = Standalone

When running the archive service standalone, the properties for the archive service must include:

catdv.azure_archive.licence_code = <generated licence code for plugin>

catdv.azure_archive.service_mode = Standalone

catdv.rest_api.host = <CatDV server hostname>

catdv.rest_api.port = <CatDV API port>

catdv.rest_api.user = <CatDV API user>

catdv.rest_api.password = <CatDV API user password> (** exclude if blank, for dev environment)

Typical rest API values for a development machine would be (password property omitted for a blank password): localhost, 8080, Administrator

In addition, the following optional properties can also be set:

catdv.service.api_timeout = <time_period_in_milliseconds>

Determines the timeout period after which the current connection to the CatDV API will be assumed to have timed out and therefore be replaced with a new connection (this is to pre-empt exceptions due to timeouts).

catdv.azure_archive.service_mode = InPlugin / Standalone

Determines whether the job processing service for the plugin runs inside the plugin or as a standalone process outside the CatDV server. The default value is "InPlugin".

catdv.azure_archive.restrict_command_access = NOT SET / config / all

If set, restricts the specified plugin commands to sys admin users. Can be used to hide 'config' commands only (i.e. Manage Service), or 'all' commands.

NB - this only works with CatDV client versions from 12 onwards.

catdv.azure_archive.allow_override

Enables the facility to override one or more parameters at the point of archive or restore. The comma-separated string value may include:

archiveLocation:Location:archive

restoreLocation:Location:restore

azure.container_name:Container name:archive

The middle item is the UI field label and may be modified.

The default value is azure.container_name:Container name:archive" Set to 'none' to allow no overrides.

catdv.azure_archive.location_mapping

Method for generating the archive file location on Azure. Valid values are:

batchMirror: The file(s) selected for archive are batched together in a date and time stamped directory (format /yyyy-mm-dd/hh:mm:ss.mmm) and mirror their src file path(s) within the batch directory. This has the effect of versioning uploads to Azure as a new copy is written for each transfer.

mirror: The archive location always mirrors the src file path which means a file is replaced each time it is archived. **NB The downside of this approach is that it does not cater for files with the same path on different file systems.

mediaStore:

mediaStoreRelative: generates relative file paths from the relevant media store(s). If no matching media store path is found, the archive location is generated using the 'mirror' approach.

mediaStoreAbsolute: As 'mediaStoreRelative' but also prepends the path with the name of the media store.

The default is 'batchMirror'.

catdv.azure_archive.days_to_display = <number_of_days>

The number of days into the past for which jobs are listed in the job queue. Any job updated in this time period is listed. The default value is 10.

catdv.azure_archive.max_jobs_to_display = <number_of_jobs>

The maximum number of jobs which will be listed in the job queue. This overrides days_to_display. The default value is 1000.

catdv.azure_archive.loop_delay = <time_period_in_milliseconds>

Determines the frequency with which the archive service checks the Job queue and attempts to run outstanding Jobs. The default value is 30000, equivalent to 30 seconds.

catdv.azure_archive.max_jobs_running_delay = <time_period_in_milliseconds>

Determines the time period to wait before attempting to run another job when the maximum number of transfers are already running. The default value is 0. This would typically be increased (either when a typical transfer is likely to be slow or when the concurrent transfer limit is greater than 1) in order to reduce hits to the database to query jobs whilst max transfers are running.

catdv.azure_archive.num_delayed_job_retries

The number of times an waiting job is retried at an interval which increases exponentially. The default value is 10, which is equivalent to roughly 34 hours elapsed running time of the archive service.

catdv.azure_archive.max_retry_delay = <time_period_in_milliseconds>

Limits the maximum time period between retries of waiting jobs. The default value is 0, which represents no maximum value.

catdv.azure_archive.retry_in_progress_job_delay

Determines the time period after which a Job which is running will be resumed (if possible) or restarted if it has not been updated during that period. Defaults to a value equivalent to 1 hour.

catdv.azure_archive.concurrent_transfer_limit

Determines the number of transfers that the archive service will attempt to run concurrently. The default value is 4.

catdv.azure_archive.concurrent_request_count

The number of parts (of a multi-part upload) that the Azure SDK will attempt to upload simultaneously. The default value is 1. Increase this value in order to upload a single large blob quickly.

catdv.azure_archive.debug

Toggles debug logging for the Azure archive plugin. The default value is "false". Set to "true" to enable debug logging.

Azure Cloud Setup / Configuration values

In order to use the plugin, an Azure Cloud account/subscription is required. If you need to set up an Azure Cloud account for testing purposes, follow the link 'Start free' from https://azure.microsoft.com/en-us/free/?b=17.01. Then the most straightforward way to proceed is to use an existing Microsoft account.

NB - Whilst Azure Cloud accounts have a free tier, this is only for app plans. After the 30 day free period standard pay as you go costs apply to Storage Accounts (of which there may be many associated with a single Azure Cloud account). The costs vary depending on the configuration of each Storage Account, so it is important to ensure that appropriate settings are configured as suggested below.

NB For development purposes on Windows machines only an Azure Simulator is available:

download: https://go.microsoft.com/fwlink/?linkid=717179&clcid=0x409

instructions: https://docs.microsoft.com/en-us/azure/storage/storage-use-emulator

Once you are logged into an Azure Cloud account, https://portal.azure.com/ will take you to the Azure console.

In order for the plugin to copy to / move to / restore from your Azure account, you will need to set up a Storage Account with at least one container.

NB - if the customer has an Azure virtual machine, there will already be a storage account. The storage account name will be based on the virtual machine name. See the Azure Virtual Machines documentation for more details.

NB - if customers need help setting up a Storage Account for production see:

https://docs.microsoft.com/en-us/azure/storage/storage-create-storage-account

https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-deployment-model

https://azure.microsoft.com/en-us/pricing/calculator/

To set up a Storage Account ** for development or test purposes **:

Sign into the Azure Portal: https://portal.azure.com/

Click 'Storage Accounts' on LHS, then click '+ Add'

Then configure optimally for development / test:

Name = e.g. catdvtest<yourname>

lowercase alphanumeric, must be globally unique across all Azure Cloud accounts

Deployment model = Resource Manager

Mandatory for Blob storage account

Account kind = Blob storage

best option for storing media files

cheaper than standard

Replication option = LRS

Access Tier = Hot

Subscription = free trial (or pay as you go, if your 30 day free trial has expired)

for pay as you go, you must upgrade to a 'pay as you go' subscription via 'My Account'

Blob pricing: https://azure.microsoft.com/en-us/pricing/details/storage/blobs/

Resource group: create new = e.g. catdvtest<yourname>

Location = West Europe

Most comprehensive service availability of european regions

Tick pin to dashboard

Click 'Create'

To get the account (access) keys for a Storage Account:

Sign into the Azure Portal: https://portal.azure.com/

Click 'Storage Accounts' on LHS

Click 'Access keys' under 'Settings' in the central menu

Click the copy button for the primary or secondary key on the RHS

To create a container in a Storage Account

Sign into the Azure Portal: https://portal.azure.com/

Click 'Storage Accounts' on LHS

Click on the storage account the container is to be added to

Click '+ Container' in toolbar at the top of the RHS overview area

Enter a container name. A container name must be a valid DNS name, conforming to the following naming rules:

Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.

Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.

All letters in a container name must be lowercase.

Container names must be from 3 through 63 characters long.

Click 'Create'

The values required to configure the service in the Azure Cloud plugin are:

account name: Name of a Storage Account which belongs to your Azure Cloud account

account key: Primary or Secondary key of the Storage Account (see above)

default container name - name of an accessible container on the Azure Storage Account which can be used to verify the connection, used as the default container the first time a user attempts to copy / move files to Azure Cloud

Using the plugin

The plugin comprises various commands that are available from the Tools menu in the client.

File transfers are initiated by the schedule copy / move / restore commands but are carried out by a separate process. This means that the client does not automatically reflect updates to a clip on completion of a transfer. Use 'Server->Refresh Window' in the client to pick up any changes during / after transfer.

The plugin includes the following commands:

Manage Azure Cloud Archive Service - View full status details of the archive service and enter / update the configuration values required to access the Azure Cloud archive. These include region / endpoint, access key, secret key and default container name. If a connection to Azure Cloud cannot be made an error is displayed and the values are not saved.

View Azure Cloud Archive Service Job Queue - list of jobs that can be filtered by status (Pending, Complete, Failed, All). It provides the capability to view job contents, view job results and cancel waiting or queued jobs.

Schedule copy to Azure Cloud - adds a copy job for each clip selected (if multiple clips reference the same source media only one job is created). Copy jobs can be scheduled when the archive service is not running or is offline but will only be run when the archive service is online (running and Azure Cloud is accessible). When the copy job is run, the source media associated with the clip is copied from the file system to Azure Cloud and the original file is preserved.

Schedule move to Azure Cloud - as copy but on successful transfer of the file to storage the job attempts to delete the original file. If for some reason the deletion fails this does not affect the job status (or archive status of the clip) but "Purge failed" is recorded in the job data.

Schedule restore from Azure Cloud - adds a restore job for each clip selected (if multiple clips reference the same source media only one job is created). Restores can be scheduled when the archive service is not running or is offline but will only be run when the archive service is online (running and Azure Cloud is accessible). When the restore job is run, the source media associated with the clip is copied from Azure Cloud to the file system.

Purge files archived to Azure Cloud - deletes the source media associated with the selected clips if they have been successfully archived.

Archive details pane

The plugin automatically creates a panel entitled "Azure Cloud Archive" containing the clip metadata which describes it's Azure Cloud archive state, including:

squarebox.catdv.archive.Azure.serviceType - Type of service responsible for file transfer

squarebox.catdv.archive.Azure.serviceName - Name of service responsible for file transfer

squarebox.catdv.archive.Azure.status - Archive status

squarebox.catdv.archive.Azure.location - Location of file in storage

squarebox.catdv.archive.Azure.date - Date (timestamp) of latest change in archive status

squarebox.catdv.archive.Azure.dateLastArchived - Date last archived

squarebox.catdv.archive.Azure.numArchives - The number of times the clip has been successfully archived

squarebox.catdv.archive.Azure.archiveKey - Identifier of file in storage

squarebox.catdv.archive.Azure.batchID - Identifies the batch of files with which the clip was archived

squarebox.catdv.archive.Azure.jobID - Identifier of current / latest archive job

squarebox.catdv.archive.Azure.parentJobID - n/a for Azure Cloud (related to bulk archives)

squarebox.catdv.archive.Azure.userId - ID of user initiating current / latest transfer

squarebox.catdv.archive.Azure.history - Record of all archive activity

squarebox.catdv.archive.Azure.accountName - Name of the Azure Cloud Storage Account

squarebox.catdv.archive.Azure.containerName - Name of container to transfer file to / from

Known Issues

Sometimes when updating the service configuration or when there is a network outage the error message provided by the Azure Cloud SDK is generic and not helpful.

Licence Code

IMPORTANT: You may install and use this software only in accordance with the terms of the CatDV Server 7.1.1 license agreement.

Square Box Systems Ltd.

February 2017

Release notes

1.4.17 (2019-03-05)

- Prevent spurious NPE error when no restore directory can be extracted from the restore location

- Update thread handling when processing jobs, to ensure a completed job will not be retried before it's status has been fully updated

- Display 'Retry' for status of unsuccessful job results if the job is being / will be retried

- Improve performance of Job Queue command for large lists of jobs

- Fix to cause job creation to fail if no source media are updated with the archive details for the job

1.4.14p1 (2019-01-25)

- Fix for issue of jobs stuck in running / stalled state

- Trim trailing path separators from archive / restore location overrides

1.4.14 (2019-01-20)

- Add plugin command that can be triggered via the rest API to generate clip data for a file archived outside CatDV

- Update README with correct version of required worker plugin (applicable from release 1.4.12)

- Fix to ensure plugin calls server rest API as /catdv/api/... instead of /api/... to prevent failed calls due to redirection of POST requests in some environments

1.4.13 (2018-11-08)

- Add config param 'catdv.azure_archive.max_retry_delay' to limit the delay period between retries of waiting jobs.

1.4.12 (2018-10-17)

- Add config param 'catdv.azure_archive.purge_directories' to turn off purging of directories when moving / purging files.

- Update README to cover installing and updating the archive worker plugin, now included as part of the

plugin installation.

1.4.11

Upgrade

- If the 'catdv.azure_archive.allow_override' server property is explicitly set to include

'location:Location:archive', modify it to 'archiveLocation:Location:archive'

Changes

- Add option to restore files to a specified location / directory (can be enabled as an override for restore)

1.4.10 (2018-09-27)

- Archive tier support - on restore, automatically request rehydration to HOT for assets in the archive tier

- Update Azure Storage for Java library to v8

1.4.9 (2018-09-14)

- Fix for error message on cancel job contains NullPointerException

- Fix for issue which causes a new FieldGroup for an archive plugin to be created each time the server is restarted

- Fixed bug causing purge commands to fail from the web UI

- Improve the error reporting when attempting to archive offline files

1.4.8betap3 (2018-07-11)

- Fix build / jar files

1.4.8betap2

- Fix location override for archives triggered from worker

- Record archive failure in clip archive status if clip has never been archived

1.4.8betap1 (2018-06-20)

Upgrade

- The last release of the Azure plugin was 1.3.4beta3, Mar-2017. If upgrading from that version (or earlier) please ensure you execute the Upgrade steps from 1.3.5beta, 1.4.1beta and 1.4.2beta.

Changes

- README update

- Fix to eliminate spurious exception starting / stopping standalone service

- Fix to eliminate spurious logging issue

1.4.8beta (2018-06-19)

- Manage service command: preserve new connection details even if they are not valid / updated, to ease entry

- Manage service command: obfuscate the account key value

1.4.7beta

- Additional location mapping option to determine location on archive from Media Store paths, includes mediaStoreRelative and mediaStoreAbsolute.

- Fix to ensure that location on archive does not contain '\' separators, regardless of location mapping in use

- Fix for NPE when clip has media file but no metadata (introduced by fix in 1.4.5)

- Integrate with ServicePlugin framework

1.4.5 (2018-05-03)

- Fix for rest.NotFoundException scheduling jobs: change method for checking job exists, to avoid server incompatibility

1.4.5beta

- Move packages in order to split out re-usable plugin SDK classes

- Fix to prevent NPE - on archiving / restoring, skip clips that have no source media (i.e. sequences)

- Fix for location override in WorkerBackupCommand

1.4.4beta

- Upgraded httpclient and httpcore libraries (required to support other archive plugin)

1.4.3beta (2018-02)

- Add config param 'catdv.azure_archive.allow_override' to enable override of archive location from the UI.

- Add config param 'catdv.azure_archive.location_mapping' to enable batching of file archive locations to be turned off - i.e. subsequent archives replace the existing file(s), rather than creating new 'date / time stamped' copies.

- Fix to ensure server config values that affect the UI are picked up on startup when archive service is standalone.

- Fix exponential back off timing for archive job retries

- Add date last restored as an archive parameter

1.4.2betap1 (2018-01-22)

- Fix for archive service start

1.4.2beta (2018-01-09)

Upgrade

- run catdvarchiveplugins1.4.2.sql against the CatDV DB to rename a job data field and to update the textual job and clip archive status values for consistency

Changes

- Add capability to run plugin archive service standalone, via config param catdv.s3archive_service.service_mode

- Provision for providing more detailed status information for jobs and (archiving) files

- Add detailed job status and job data to Job Details pane in the Service Job Queue UI

- New config param 'catdv.azure_archive.max_jobs_to_display' for configuring the maximum number of jobs which will be listed in the job queue. This overrides days_to_display and defaults to 1000

- Increased default value for 'catdv.azure_archive.loop_delay' to 30 seconds

- Increased default value for 'catdv.s3archive_service.concurrent_transfer_limit' to 4

- Increased default value for 'catdv.s3archive_service.days_to_display' to 10

1.4.1betap6 (2017-12-22)

- Fixes for worker plugin commands

1.4.1betap5 (2017-12-20)

- Return json response message from WorkerPurgeCommand

- Include metaclipID where applicable in error details for worker backup / restore / purge commands

1.4.1betap4 (2017-12-12)

- Fix bulk backup and restore commands to capture and report unexpected errors queuing child jobs

- Update worker backup and restore commands to return a JSON representation of the archive result in the command response.

1.4.1betap3 (2017-12-01)

- Fix to ignore clips with duplicate mapped file paths, before processing to queue jobs

1.4.1betap2

- New hidden versions of backup (copy/move) and purge commands for use by worker

1.4.1betap1 (2017-11-16)

- Fix to ensure archive metadata for source media with matching media paths are updated simultaneously

- Additional service logging, new config param 'catdv.azure_archive.debug' now turns on/off most logging after initial plugin startup

1.4.1beta (2017-06-09)

Upgrade

- remove 'catdv.rest_api.*' config properties in Server Config on CatDV control panel

Changes

- major update to job queue command UI, utilising new features provided by version 3 of plugin framework

- make API calls in process (inside server) from plugin commands and from archive service when running inside server (depends on version 3 of plugin framework)

1.3.5beta (2017-06-09)

Upgrade

- change config property 'catdv.rest_api.client_key' to 'catdv.azure_archive.licence_code' in Server Config on CatDV control panel

- run catdvarchiveplugins1.3.5.sql against the CatDV DB to fix the field type for archive date fields

Changes

- improve job processing loop so that job priorities are always respected: only take one job from the queue at a time, when waiting job delays have elapsed re-queue jobs rather than processing waiting jobs directly (note re-queued jobs of equal priority will be processed before newer jobs, as the lowest job id will be processed first)

- ensure that transfers to Azure never block the job processing loop, keeping the connection status up to date

- enable processing of multiple concurrent transfers plus config param max_jobs_running_delay for optimisation

- improve handling when Azure is unavailable: after a short period the service status updates to "Running (offline)"

- restart orphaned "in progress" jobs (e.g. from a server restart) more quickly

- Option to enter container name when copying / moving files to Azure. The container name on the Manage Service screen is now only the default value filled in the first time a user attempts to copy or move files. Once the user has entered a container, a subsequent move / copy will default to the last value entered. The container warning and "Apply container to queued jobs" checkbox are no longer on the Manage Service screen as changes there now only affect the default container for a new user of the archive plugin.

- Option to specify destination location 'path' when copying / moving files to Azure from the worker.

- Add config param "catdv.azure_archive.restrict_command_access" which can be used to hide 'all' plugin commands or 'config' commands (currently Manage Service). See README for details.

- switch to using statusCode as primary status value (except job queries which requires latest server)

- add support for pausing a service (processing transfers)

- improve plugin licence handling

- Fix archive date fields in desktop client

- fix NPE when clip has no metadata

1.3.4beta3 (2017-03-02)

- Fix restore issue relating to "The process cannot access the file because it is being used by another process."

1.3.4beta2 (2017-03-02)

- Fix move command so that purge file succeeds after transfer

- Add missing apache commons lang library for wrapping messages

1.3.4beta (2017-02-14)

Initial beta version

Copyright © Square Box Systems Ltd. 2002-2019. All rights reserved.