Initial commit
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
11
utils/60-persistent-storage-cas-load.rules
Normal file
11
utils/60-persistent-storage-cas-load.rules
Normal file
@@ -0,0 +1,11 @@
|
||||
ACTION!="add", GOTO="cas_loader_end"
|
||||
SUBSYSTEM!="block", GOTO="cas_loader_end"
|
||||
|
||||
RUN+="/lib/opencas/open-cas-loader /dev/$name"
|
||||
|
||||
# Work around systemd<->udev interaction, make sure filesystems with labels on
|
||||
# cas are mounted properly
|
||||
KERNEL!="cas*", GOTO="cas_loader_end"
|
||||
IMPORT{builtin}="blkid"
|
||||
ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", RUN+="/lib/opencas/open-cas-mount-utility $env{ID_FS_LABEL_ENC}"
|
||||
LABEL="cas_loader_end"
|
||||
38
utils/60-persistent-storage-cas.rules
Normal file
38
utils/60-persistent-storage-cas.rules
Normal file
@@ -0,0 +1,38 @@
|
||||
ACTION=="remove", GOTO="cas_end"
|
||||
|
||||
SUBSYSTEM=="block", KERNEL=="cas*", OPTIONS+="watch"
|
||||
|
||||
SUBSYSTEM!="block", GOTO="cas_end"
|
||||
KERNEL!="cas*", GOTO="cas_end"
|
||||
|
||||
# ignore partitions that span the entire disk
|
||||
TEST=="whole_disk", GOTO="cas_end"
|
||||
|
||||
# for partitions import parent information
|
||||
ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"
|
||||
|
||||
# by-path
|
||||
ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
|
||||
ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
|
||||
|
||||
# probe filesystem metadata of disks
|
||||
KERNEL=="cas*", IMPORT{builtin}="blkid"
|
||||
|
||||
# by-label/by-uuid links (filesystem metadata)
|
||||
ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}", OPTIONS+="link_priority=999"
|
||||
ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}", OPTIONS+="link_priority=999"
|
||||
|
||||
# by-id (World Wide Name)
|
||||
ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n"
|
||||
|
||||
# by-partlabel/by-partuuid links (partition metadata)
|
||||
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}", OPTIONS+="link_priority=999"
|
||||
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}", OPTIONS+="link_priority=999"
|
||||
|
||||
# add symlink to GPT root disk
|
||||
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root", OPTIONS+="link_priority=999"
|
||||
|
||||
LABEL="cas_end"
|
||||
|
||||
71
utils/Makefile
Normal file
71
utils/Makefile
Normal file
@@ -0,0 +1,71 @@
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
CASCTL_DIR = /lib/opencas
|
||||
UDEVRULES_DIR = /lib/udev/rules.d
|
||||
UDEV:=$(shell which udevadm)
|
||||
SYSTEMCTL := $(shell which systemctl)
|
||||
|
||||
ifeq (, $(shell which systemctl))
|
||||
define cas_install
|
||||
install -m 755 open-cas-shutdown /etc/init.d/open-cas-shutdown
|
||||
/sbin/chkconfig open-cas-shutdown on; service open-cas-shutdown start
|
||||
endef
|
||||
else
|
||||
ifneq "$(wildcard /usr/lib/systemd/system)" ""
|
||||
SYSTEMD_DIR=/usr/lib/systemd/system
|
||||
else
|
||||
SYSTEMD_DIR=/lib/systemd/system
|
||||
endif
|
||||
define cas_install
|
||||
install -m 644 open-cas-shutdown.service $(SYSTEMD_DIR)/open-cas-shutdown.service
|
||||
install -m 755 -d $(SYSTEMD_DIR)/../system-shutdown
|
||||
install -m 755 open-cas.shutdown $(SYSTEMD_DIR)/../system-shutdown/open-cas.shutdown
|
||||
$(SYSTEMCTL) daemon-reload
|
||||
$(SYSTEMCTL) -q enable open-cas-shutdown
|
||||
endef
|
||||
endif
|
||||
|
||||
# Just a placeholder when running make from parent dir without install/uninstall arg
|
||||
default: ;
|
||||
|
||||
install:
|
||||
@echo "Installing Open-CAS utils"
|
||||
|
||||
@install -m 755 -d $(CASCTL_DIR)
|
||||
@install -m 644 opencas.py $(CASCTL_DIR)/opencas.py
|
||||
@install -m 755 casctl $(CASCTL_DIR)/casctl
|
||||
@install -m 755 open-cas-loader $(CASCTL_DIR)/open-cas-loader
|
||||
@install -m 755 open-cas-mount-utility $(CASCTL_DIR)/open-cas-mount-utility
|
||||
|
||||
@ln -fs $(CASCTL_DIR)/casctl /sbin/casctl
|
||||
|
||||
@install -m 644 60-persistent-storage-cas-load.rules $(UDEVRULES_DIR)/60-persistent-storage-cas-load.rules
|
||||
@install -m 644 60-persistent-storage-cas.rules $(UDEVRULES_DIR)/60-persistent-storage-cas.rules
|
||||
|
||||
@install -m 755 -d /usr/share/doc/opencas
|
||||
|
||||
@$(UDEV) control --reload-rules
|
||||
|
||||
@install -m 644 casctl.8 /usr/share/man/man8/casctl.8
|
||||
|
||||
$(cas_install)
|
||||
|
||||
uninstall:
|
||||
@rm $(CASCTL_DIR)/opencas.py
|
||||
@rm $(CASCTL_DIR)/casctl
|
||||
@rm $(CASCTL_DIR)/open-cas-loader
|
||||
@rm $(CASCTL_DIR)/open-cas-mount-utility
|
||||
@rm -rf $(CASCTL_DIR)
|
||||
|
||||
@rm /sbin/casctl
|
||||
|
||||
@rm /usr/share/man/man8/casctl.8
|
||||
|
||||
@rm /lib/udev/rules.d/60-persistent-storage-cas-load.rules
|
||||
@rm /lib/udev/rules.d/60-persistent-storage-cas.rules
|
||||
|
||||
|
||||
.PHONY: install uninstall clean distclean
|
||||
565
utils/casadm.8
Normal file
565
utils/casadm.8
Normal file
@@ -0,0 +1,565 @@
|
||||
.TH casadm 8 __CAS_DATE__ v__CAS_VERSION__
|
||||
.SH NAME
|
||||
casadm \- create, and manage Open CAS instances
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
\fBcasadm\fR <command> [options...]
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright(c) 2012-2019 by the Intel Corporation.
|
||||
|
||||
.SH DESCRIPTION
|
||||
Open Cache Acceleration Software (CAS) accelerates Linux applications by caching
|
||||
active (hot) data to a local flash device inside servers. Open CAS implements
|
||||
caching at the server level, utilizing local high-performance flash media as
|
||||
the cache drive media inside the application server as close as possible to
|
||||
the CPU, thus reducing storage latency as much as possible.
|
||||
.PP
|
||||
Open Cache Acceleration Software installs into the GNU/Linux operating system itself,
|
||||
as a kernel module. The nature of the integration provides a cache solution that is
|
||||
transparent to users and applications, and your existing storage infrastructure. No
|
||||
storage migration effort or application changes are required.
|
||||
.PP
|
||||
\fBCache device\fR is a faster drive (e.g. SSD-type) used for speeding-up core device.
|
||||
.br
|
||||
\fBCore device\fR is a slower drive (e.g. HDD-type) that will be accelerated by Open CAS.
|
||||
|
||||
.SH MODES
|
||||
Open CAS caching software has several modes of operation:
|
||||
.TP
|
||||
.B Write-Through (wt)
|
||||
Write-Through is a basic caching mode where writes are done synchronously to
|
||||
the cache device and to the core device. Write-Through cache, which is also known
|
||||
as Read Cache, mainly improves performance of read IO operations.
|
||||
|
||||
.TP
|
||||
.B Write-Back (wb)
|
||||
In Write-Back mode writes are initially written to the cache device only. Cached
|
||||
write operations that are not synchronized with core device are marked as dirty.
|
||||
The procedure of writing dirty data from cache device to core device is known as
|
||||
cleaning. Cleaning may be required if cache is full and eviction (replacement)
|
||||
policy needs to remove stale data to make space for incoming blocks. Open CAS
|
||||
provides mechanism which automatically cleans dirty data in background. This is
|
||||
cleaning (flushing) thread. User can also invoke manual cleaning procedure (see
|
||||
-E, --flush-cache and -F --flush-core options). Write-Back cache, also known as
|
||||
Write Cache, improves performance of both read and write IO operations.
|
||||
|
||||
.TP
|
||||
.B Write-Around (wa)
|
||||
In Write-Around mode write operations are not cached. This means that write to
|
||||
block that does not exist in cache is written directly to the core device,
|
||||
bypassing the cache. If write operation is issued to the block which is already
|
||||
in cache (because of previous read operation) then write is send to the core device
|
||||
and cache block is updated in the cache device. Write-Around cache improves performance
|
||||
of workloads where write operation is done rarely and no further read accesses
|
||||
to that data are performed, so there is no point in caching it.
|
||||
|
||||
.TP
|
||||
.B Pass-Through (pt)
|
||||
In Pass-Through mode all read and write operations are not cached and sent directly
|
||||
to the core device. Pass-Through mode may be used in case if user doesn't want to
|
||||
cache any workload, for example in case if there are some maintenance operations
|
||||
causing cache pollution.
|
||||
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.B -S, --start-cache
|
||||
Start cache instance.
|
||||
|
||||
.TP
|
||||
.B -T, --stop-cache
|
||||
Stop cache instance.
|
||||
|
||||
.TP
|
||||
.B -X, --set-param
|
||||
Set runtime parameter for cache/core instance.
|
||||
|
||||
.TP
|
||||
.B -G, --set-param
|
||||
Get runtime parameter for cache/core instance.
|
||||
|
||||
.TP
|
||||
.B -Q, --set-cache-mode
|
||||
Switch caching mode of cache instance.
|
||||
|
||||
.TP
|
||||
.B -A, --add-core
|
||||
Add core device to cache instance.
|
||||
|
||||
.TP
|
||||
.B -R, --remove-core
|
||||
Remove core device from cache instance.
|
||||
|
||||
.TP
|
||||
.B " "--remove-detached
|
||||
Remove core device from core pool.
|
||||
|
||||
.TP
|
||||
.B -L, --list-caches
|
||||
List all cache instances and core devices.
|
||||
|
||||
.TP
|
||||
.B -P, --stats
|
||||
Print statistics of cache instance.
|
||||
|
||||
.TP
|
||||
.B -Z, --reset-counters
|
||||
Reset statistics of given cache/core instance.
|
||||
|
||||
.TP
|
||||
.B -F, --flush-cache
|
||||
Flush all dirty data from the caching device to core devices.
|
||||
|
||||
.TP
|
||||
.B -E, --flush-core
|
||||
Flush dirty data of a given core from the caching device to this core device.
|
||||
|
||||
.TP
|
||||
.B -C, --io-class {--load-config|--list}
|
||||
Manage IO classes.
|
||||
.br
|
||||
|
||||
1. \fB-C, --load-config\fR - load default configuration of IO classes.
|
||||
\fBNOTE:\fR See /etc/opencas for example configuration file.
|
||||
|
||||
2. \fB-L, --list\fR - print current IO class configuration. Allowed output formats: table or CSV.
|
||||
|
||||
.TP
|
||||
.B -N, --nvme
|
||||
Manage NVMe device.
|
||||
.br
|
||||
|
||||
1. \fB-F, --format\fR <MODE> {normal|atomic} - format NVMe device to one of supported modes.
|
||||
\fBNOTE:\fR After formatting NVMe device platform reboot is required.
|
||||
|
||||
.br
|
||||
Defines cache metadata mode.
|
||||
In normal mode NVMe namespace uses LBA of size 512 bytes. Cache data and metadata
|
||||
are written to disk in separate requests in the same way that it happens with
|
||||
standard SSD device.
|
||||
Atomic mode exploits extended NVMe metadata features. In this mode namespace
|
||||
uses 520 bytes LBA allowing to write cache data and metadata in a single
|
||||
request (atomically).
|
||||
|
||||
.TP
|
||||
.B -H, --help
|
||||
Print help.
|
||||
|
||||
.TP
|
||||
.B -V, --version
|
||||
Print Open CAS product version.
|
||||
|
||||
.SH OPTIONS
|
||||
List of available options depends on current context of invocation. For each
|
||||
command there is a different list of available options:
|
||||
|
||||
.BR
|
||||
|
||||
.SH Options that are valid with --start-cache (-S) are:
|
||||
|
||||
.TP
|
||||
.B -d, --cache-device <DEVICE>
|
||||
Path to caching device to be used e.g. SSD device (/dev/sdb).
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Unique identifier of cache (if not provided the first available will be used) <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -l, --load
|
||||
If metadata exists on a device and this parameter is used, cache will be started based on information from metadata.
|
||||
If this parameter is not used, cache will be started with full initialization of new metadata.
|
||||
This option should be used if dirty data were not flushed on exit (if the cache was stopped with the -n, --no-data-flush option).
|
||||
|
||||
\fBCAUTION:\fR
|
||||
.br
|
||||
\fB*\fR If the data between the cache device and core device is not in sync (e.g. changes between cache stop and load operations), starting
|
||||
cache with load option may cause data mismatch.
|
||||
|
||||
.TP
|
||||
.B -f, --force
|
||||
Force to start a cache. By default cache will not be started if utility detects file system on cache device.
|
||||
This parameter ignores this situations, and starts a cache instance.
|
||||
|
||||
.TP
|
||||
.B -c, --cache-mode {wt|wb|wa|pt}
|
||||
Cache mode to be used for a cache instance.
|
||||
|
||||
Available modes are:
|
||||
.br
|
||||
1. \fBwt - Write-Through (default)\fR.
|
||||
.br
|
||||
2. \fBwb - Write-Back\fR.
|
||||
.br
|
||||
3. \fBwa - Write-Around\fR.
|
||||
.br
|
||||
4. \fBpt - Pass-Through\fR.
|
||||
|
||||
.TP
|
||||
.B -x, --cache-line-size <NUMBER>
|
||||
Set cache line size for given cache instance, expressed in KiB. This
|
||||
can't be reconfigured runtime. Allowed values: {4,8,16,32,64}
|
||||
(default: 4)
|
||||
|
||||
.SH Options that are valid with --stop-cache (-T) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -n, --no-data-flush
|
||||
Do not flush dirty data on exit (may be \fBDANGEROUS\fR).
|
||||
If this option was used, the cache should be restarted with the -l, --load option.
|
||||
.br
|
||||
\fBNOTE:\fR If dirty data were not flushed, the contents of a core device
|
||||
MUST NOT be changed before restarting the cache. Otherwise there is
|
||||
a data mismatch risk.
|
||||
|
||||
.SH Options that are valid with --set-param (-X) are:
|
||||
|
||||
.TP
|
||||
.B -n, --name <NAME>
|
||||
Name of parameters namespace.
|
||||
|
||||
Available namespaces are:
|
||||
.br
|
||||
\fBseq-cutoff\fR - Sequential cutoff parameters.
|
||||
\fBcleaning\fR - Cleaning policy parameters.
|
||||
\fBcleaning-alru\fR - Cleaning policy ALRU parameters.
|
||||
\fBcleaning-acp\fR - Cleaning policy ACP parameters.
|
||||
|
||||
.SH Options that are valid with --set-param (-X) --name (-n) seq-cutoff are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance. If this option
|
||||
is not specified, parameter is set to all cores within given cache instance.
|
||||
|
||||
.TP
|
||||
.B -t, --seq-threshold <NUMBER>
|
||||
Amount of sequential data in KiB after which request is handled in pass-through mode.
|
||||
|
||||
.TP
|
||||
.B -p, --seq-policy {always|full|never}
|
||||
Sequential cutoff policy to be used with a given core instance(s).
|
||||
|
||||
.SH Options that are valid with --set-param (-X) --name (-n) cleaning are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -p, --policy {nop|alru|acp}
|
||||
Cleaning policy type to be used with a given cache instance.
|
||||
|
||||
Available policies:
|
||||
.br
|
||||
1. \fBnop\fR. No Operation (no periodical cleaning, clean on eviction only).
|
||||
.br
|
||||
2. \fBalru\fR. Approximately Least Recently Used (default).
|
||||
.br
|
||||
3. \fBacp\fR. Aggressive Cleaning Policy.
|
||||
|
||||
.SH Options that are valid with --set-param (-X) --name (-n) cleaning-alru are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -w, --wake-up <NUMBER>
|
||||
Period of time between awakenings of flushing thread [s] (default: 20 s).
|
||||
|
||||
.TP
|
||||
.B -s, --staleness-time <NUMBER>
|
||||
Time that has to pass from the last write operation before a dirty cache block can be scheduled to be flushed [s] (default: 120 s).
|
||||
|
||||
.TP
|
||||
.B -b, --flush-max-buffers <NUMBER>
|
||||
Number of dirty cache blocks to be flushed in one cleaning cycle (default: 100).
|
||||
|
||||
.TP
|
||||
.B -t, --activity-threshold <NUMBER>
|
||||
Cache idle time before flushing thread can start [ms] (default: 10000 ms).
|
||||
|
||||
.SH Options that are valid with --set-param (-X) --name (-n) cleaning-acp are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -w, --wake-up <NUMBER>
|
||||
Period of time between awakenings of flushing thread [ms] (default: 10 ms).
|
||||
|
||||
.TP
|
||||
.B -b, --flush-max-buffers <NUMBER>
|
||||
Number of dirty cache blocks to be flushed in one cleaning cycle (default: 128).
|
||||
|
||||
.SH Options that are valid with --get-param (-G) are:
|
||||
|
||||
.TP
|
||||
.B -n, --name <NAME>
|
||||
Name of parameters namespace.
|
||||
|
||||
Available namespaces are:
|
||||
.br
|
||||
\fBseq-cutoff\fR - Sequential cutoff parameters.
|
||||
\fBcleaning\fR - Cleaning policy parameters.
|
||||
\fBcleaning-alru\fR - Cleaning policy ALRU parameters.
|
||||
\fBcleaning-acp\fR - Cleaning policy ACP parameters.
|
||||
|
||||
.SH Options that are valid with --get-param (-G) --name (-n) seq-cutoff are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance.
|
||||
|
||||
.TP
|
||||
.B -o, --output-format {table|csv}
|
||||
Defines output format for parameter list. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --get-param (-G) --name (-n) cleaning are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -o, --output-format {table|csv}
|
||||
Defines output format for parameter list. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --get-param (-G) --name (-n) cleaning-alru are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -o, --output-format {table|csv}
|
||||
Defines output format for parameter list. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --get-param (-G) --name (-n) cleaning-acp are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -o, --output-format {table|csv}
|
||||
Defines output format for parameter list. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --set-cache-mode (-Q) are:
|
||||
.TP
|
||||
.B -c, --cache-mode {wt|wb|wa|pt}
|
||||
Cache mode to be used with a given cache instance.
|
||||
|
||||
Available modes:
|
||||
.br
|
||||
1. \fBwt - Write-Through\fR.
|
||||
.br
|
||||
2. \fBwb - Write-Back\fR.
|
||||
.br
|
||||
3. \fBwa - Write-Around\fR.
|
||||
.br
|
||||
4. \fBpt - Pass-Through\fR.
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -f, --flush-cache {yes|no}
|
||||
Flush all cache dirty data before switching to different mode. Option is required
|
||||
when switching from Write-Back mode.
|
||||
|
||||
.SH Options that are valid with --add-core (-A) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -d, --core-device <DEVICE>
|
||||
Path to core device e.g. HDD device.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance for new core to be created. This
|
||||
parameter is optional. If it is not supplied, first available core id within cache instance will
|
||||
be used for new core.
|
||||
|
||||
.SH Options that are valid with --remove-core (-R) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance.
|
||||
|
||||
.TP
|
||||
.B -f, --force
|
||||
Force remove inactive core.
|
||||
|
||||
.SH Options that are valid with --remove-detached are:
|
||||
.TP
|
||||
.B -d, --device <DEVICE>
|
||||
Path to core device to be removed from core pool.
|
||||
|
||||
.SH Options that are valid with --list-caches (-L) are:
|
||||
.TP
|
||||
.B -o, --output-format {table|csv}
|
||||
Defines output format for list of all cache instances and core devices. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --stats (-P) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance. If this option is
|
||||
not given, aggregate statistics for whole cache instance are printed instead.
|
||||
|
||||
.TP
|
||||
.B -d, --io-class-id <ID>
|
||||
Identifier of IO class <0-33>.
|
||||
|
||||
.TP
|
||||
.B -f, --filter <FILTER-SPEC>
|
||||
Defines filters to be applied. This is comma separated (no
|
||||
white-spaces allowed) list from following set of available:
|
||||
|
||||
.br
|
||||
1. \fBconf\fR - provides information on configuration.
|
||||
.br
|
||||
2. \fBusage\fR - occupancy, free, clean and dirty statistics are printed.
|
||||
.br
|
||||
3. \fBreq\fR - IO request level statistics are printed.
|
||||
.br
|
||||
4. \fBblk\fR - block level statistics are printed.
|
||||
.br
|
||||
5. \fBerr\fR - error statistics are printed.
|
||||
.br
|
||||
6. \fBall\fR - all of the above.
|
||||
.br
|
||||
|
||||
Default for --filter option is \fBall\fR.
|
||||
|
||||
.TP
|
||||
.B -o --output-format {table|csv}
|
||||
Defines output format for statistics. It can be either \fBtable\fR
|
||||
(default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --reset-counters (-Z) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance. If this option
|
||||
is not specified, statistics are reset for all cores within given cache instance.
|
||||
|
||||
|
||||
.SH Options that are valid with --flush-cache (-F) are:
|
||||
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.SH Options that are valid with --flush-core (-E) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -j, --core-id <ID>
|
||||
Identifier of core instance <0-4095> within given cache instance.
|
||||
|
||||
.SH Options that are valid with --io-class --load-config (-C -C) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -f, --file <FILE>
|
||||
Configuration file containing IO class definition.
|
||||
|
||||
.SH Options that are valid with --io-class --list (-C -L) are:
|
||||
.TP
|
||||
.B -i, --cache-id <ID>
|
||||
Identifier of cache instance <1-16384>.
|
||||
|
||||
.TP
|
||||
.B -o --output-format {table|csv}
|
||||
Defines output format for printed IO class configuration. It can be either
|
||||
\fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
.SH Options that are valid with --nvme --format (-N -F) are:
|
||||
|
||||
.TP
|
||||
.B -d, --device <DEVICE>
|
||||
Path to NVMe device to be formatted (e.g. /dev/nvme0).
|
||||
|
||||
.TP
|
||||
.B -f, --force
|
||||
Force to format NVMe device. By default device will not be formatted if utility
|
||||
detects on the device file system or presence of dirty data after cache dirty
|
||||
shutdown. This parameter formats NVMe namespace regardless to this situations.
|
||||
|
||||
|
||||
.SH Command --help (-H) does not accept any options.
|
||||
.BR
|
||||
|
||||
.SH Options that are valid with --version (-V) are:
|
||||
|
||||
.TP
|
||||
.B -o --output-format {table|csv}
|
||||
Defines output format. It can be either \fBtable\fR (default) or \fBcsv\fR.
|
||||
|
||||
|
||||
.SH ENVIRONMENT VARIABLES
|
||||
Following environment variables affect behavior of casadm administrative utility:
|
||||
.TP
|
||||
.B LANG
|
||||
If en_US.utf-8, en_US.UTF-8 is configured, tables displayed by -L/--list-caches,
|
||||
-P/--stats and -C -L/--io-class --list are formatted using Unicode table drawing
|
||||
characters. Otherwise only '+', '|' and '-' are used.
|
||||
|
||||
.TP
|
||||
.B TERM
|
||||
If xterm or screen is used, colors are used for formatting tables. Otherwise,
|
||||
color is not used. Additionally colors are NOT used if standard output of
|
||||
casadm isn't a TTY (i.e. it's output is displayed via less(1), watch(1) or
|
||||
redirected to a file)
|
||||
|
||||
.TP
|
||||
.B CASADM_COLORS
|
||||
If this variable is set, colors are used even if TERM isn't set to xterm/screen
|
||||
or when output is redirected to another program. It's convenient to do:
|
||||
CASADM_COLORS=true screen 'casadm -P -i 1'
|
||||
|
||||
.TP
|
||||
.B CASADM_NO_LINE_BREAK
|
||||
If CASADM_NO_LINE_BREAK is set, casadm won't break lines for tables displayed
|
||||
by -L/--list-caches, -P/--stats and -C -L/--io-class --list
|
||||
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Patches and issues may be submitted to the official repository at
|
||||
\fBhttps://open-cas.github.io\fR
|
||||
|
||||
.SH SEE ALSO
|
||||
.TP
|
||||
casctl(8), opencas.conf(5)
|
||||
142
utils/casctl
Executable file
142
utils/casctl
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python2
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
import opencas
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
# Start - load all the caches and add cores
|
||||
|
||||
def start():
|
||||
try:
|
||||
config = opencas.cas_config.from_file('/etc/opencas/opencas.conf',
|
||||
allow_incomplete=True)
|
||||
except Exception as e:
|
||||
eprint(e)
|
||||
eprint('Unable to parse config file.')
|
||||
exit(1)
|
||||
|
||||
for cache in config.caches.values():
|
||||
try:
|
||||
opencas.start_cache(cache, True)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
eprint('Unable to load cache {0} ({1}). Reason:\n{2}'
|
||||
.format(cache.cache_id, cache.device, e.result.stderr))
|
||||
|
||||
# Initial cache start
|
||||
|
||||
def add_core_recursive(core, config):
|
||||
with_error = False
|
||||
if core.added:
|
||||
return with_error
|
||||
if core.marked:
|
||||
eprint('Unable to add core {0} to cache {1}. Reason:\nRecursive core configuration!'
|
||||
.format(core.device, core.cache_id))
|
||||
exit(3)
|
||||
core.marked = True
|
||||
match = re.match('/dev/cas(\d)-(\d).*', core.device)
|
||||
if match:
|
||||
cache_id,core_id = match.groups()
|
||||
with_error = add_core_recursive(config.caches[int(cache_id)].cores[int(core_id)], config)
|
||||
try:
|
||||
opencas.add_core(core, False)
|
||||
core.added = True
|
||||
except opencas.casadm.CasadmError as e:
|
||||
eprint('Unable to add core {0} to cache {1}. Reason:\n{2}'
|
||||
.format(core.device, core.cache_id, e.result.stderr))
|
||||
with_error = True
|
||||
return with_error
|
||||
|
||||
def init(force):
|
||||
exit_code = 0
|
||||
try:
|
||||
config = opencas.cas_config.from_file('/etc/opencas/opencas.conf')
|
||||
except Exception as e:
|
||||
eprint(e)
|
||||
eprint('Unable to parse config file.')
|
||||
exit(1)
|
||||
|
||||
if not force:
|
||||
for cache in config.caches.values():
|
||||
try:
|
||||
status = opencas.check_cache_device(cache.device)
|
||||
if status['Is cache'] == 'yes' and status['Cache dirty'] == 'yes':
|
||||
eprint('Unable to perform initial configuration.\n' \
|
||||
'One of cache devices contains dirty data.')
|
||||
exit(1)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
eprint('Unable to check status of device {0}. Reason:\n{1}'
|
||||
.format(cache.device, e.result.stderr))
|
||||
exit(e.result.exit_code)
|
||||
|
||||
for cache in config.caches.values():
|
||||
try:
|
||||
opencas.start_cache(cache, False, force)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
eprint('Unable to start cache {0} ({1}). Reason:\n{2}'
|
||||
.format(cache.cache_id, cache.device, e.result.stderr))
|
||||
exit_code = 2
|
||||
try:
|
||||
opencas.configure_cache(cache)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
eprint('Unable to configure cache {0} ({1}). Reason:\n{2}'
|
||||
.format(cache.cache_id, cache.device, e.result.stderr))
|
||||
exit_code = 2
|
||||
|
||||
for core in config.cores:
|
||||
core.added = False
|
||||
core.marked = False
|
||||
for core in config.cores:
|
||||
with_error = add_core_recursive(core, config)
|
||||
if with_error:
|
||||
exit_code = 2
|
||||
|
||||
exit(exit_code)
|
||||
|
||||
# Stop - detach cores and stop caches
|
||||
def stop(flush):
|
||||
try:
|
||||
opencas.stop(flush)
|
||||
except Exception as e:
|
||||
eprint(e)
|
||||
|
||||
# Command line arguments parsing
|
||||
|
||||
class cas:
|
||||
def __init__(self):
|
||||
parser = argparse.ArgumentParser(prog = 'cas')
|
||||
subparsers = parser.add_subparsers(title = 'actions')
|
||||
|
||||
parser_init = subparsers.add_parser('init', help = 'Setup initial configuration')
|
||||
parser_init.set_defaults(command='init')
|
||||
parser_init.add_argument ('--force', action='store_true', help = 'Force cache start')
|
||||
|
||||
parser_start = subparsers.add_parser('start', help = 'Start cache configuration')
|
||||
parser_start.set_defaults(command='start')
|
||||
|
||||
parser_stop = subparsers.add_parser('stop', help = 'Stop cache configuration')
|
||||
parser_stop.set_defaults(command='stop')
|
||||
parser_stop.add_argument ('--flush', action='store_true', help = 'Flush data before stopping')
|
||||
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
getattr(self, 'command_' + args.command)(args)
|
||||
|
||||
def command_init(self, args):
|
||||
init(args.force)
|
||||
|
||||
def command_start(self, args):
|
||||
start()
|
||||
|
||||
def command_stop(self, args):
|
||||
stop(args.flush)
|
||||
|
||||
if __name__ == '__main__':
|
||||
cas()
|
||||
63
utils/casctl.8
Normal file
63
utils/casctl.8
Normal file
@@ -0,0 +1,63 @@
|
||||
.TH casctl.8 __CAS_DATE__ v__CAS_VERSION__
|
||||
.SH NAME
|
||||
casctl \- whole-configuration-manager for Open CAS.
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
\fBcasctl\fR <command> [options...]
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright(c) 2012-2019 by the Intel Corporation.
|
||||
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.B start
|
||||
Start all cache instances.
|
||||
|
||||
.TP
|
||||
.B stop
|
||||
Stop all cache instances.
|
||||
|
||||
.TP
|
||||
.B init
|
||||
Initial configuration of caches and core devices.
|
||||
|
||||
.br
|
||||
.B CAUTION
|
||||
.br
|
||||
May be used if there is no metadata on cache device or if metatata exists, then only if it's all clean.
|
||||
|
||||
.TP
|
||||
.B -h, --help
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
.SH Command start does not accept any options.
|
||||
|
||||
.TP
|
||||
.SH Options that are valid with stop are:
|
||||
|
||||
.TP
|
||||
.B --flush
|
||||
Flush data before stopping.
|
||||
|
||||
.TP
|
||||
.SH Options that are valid with init are:
|
||||
|
||||
.TP
|
||||
.B --force
|
||||
Force cache start even if cache device contains partitions or metadata from previously running cache instances.
|
||||
|
||||
.TP
|
||||
.SH Command --help (-h) does not accept any options.
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Patches and issues may be submitted to the official repository at
|
||||
\fBhttps://open-cas.github.io\fR
|
||||
|
||||
.SH SEE ALSO
|
||||
.TP
|
||||
casadm(8), opencas.conf(5)
|
||||
24
utils/ext3-config.csv
Normal file
24
utils/ext3-config.csv
Normal file
@@ -0,0 +1,24 @@
|
||||
IO class id,IO class name,Eviction priority,Allocation
|
||||
0,Unclassified,22,1
|
||||
1,Superblock,0,1
|
||||
2,GroupDesc,1,1
|
||||
3,BlockBitmap,2,1
|
||||
4,InodeBitmap,3,1
|
||||
5,Inode,4,1
|
||||
6,IndirectBlk,5,1
|
||||
7,Directory,6,1
|
||||
8,Journal,7,1
|
||||
10,Xatrr,8,1
|
||||
11,<=4KiB,9,1
|
||||
12,<=16KiB,10,1
|
||||
13,<=64KiB,11,1
|
||||
14,<=256KiB,12,1
|
||||
15,<=1MiB,13,1
|
||||
16,<=4MiB,14,1
|
||||
17,<=16MiB,15,1
|
||||
18,<=64MiB,16,1
|
||||
19,<=256MiB,17,1
|
||||
20,<=1GiB,18,1
|
||||
21,>1GiB,19,1
|
||||
22,O_DIRECT,20,1
|
||||
23,Misc,21,1
|
||||
|
25
utils/ext4-config.csv
Normal file
25
utils/ext4-config.csv
Normal file
@@ -0,0 +1,25 @@
|
||||
IO class id,IO class name,Eviction priority,Allocation
|
||||
0,Unclassified,23,1
|
||||
1,Superblock,0,1
|
||||
2,GroupDesc,1,1
|
||||
3,BlockBitmap,2,1
|
||||
4,InodeBitmap,3,1
|
||||
5,Inode,4,1
|
||||
6,IndirectBlk,5,1
|
||||
7,Directory,6,1
|
||||
8,Journal,7,1
|
||||
9,Extent,8,1
|
||||
10,Xatrr,9,1
|
||||
11,<=4KiB,10,1
|
||||
12,<=16KiB,11,1
|
||||
13,<=64KiB,12,1
|
||||
14,<=256KiB,13,1
|
||||
15,<=1MiB,14,1
|
||||
16,<=4MiB,15,1
|
||||
17,<=16MiB,16,1
|
||||
18,<=64MiB,17,1
|
||||
19,<=256MiB,18,1
|
||||
20,<=1GiB,19,1
|
||||
21,>1GiB,20,1
|
||||
22,O_DIRECT,21,1
|
||||
23,Misc,22,1
|
||||
|
15
utils/ioclass-config.csv
Normal file
15
utils/ioclass-config.csv
Normal file
@@ -0,0 +1,15 @@
|
||||
IO class id,IO class name,Eviction priority,Allocation
|
||||
0,unclassified,22,1
|
||||
1,metadata&done,0,1
|
||||
11,file_size:le:4096&done,9,1
|
||||
12,file_size:le:16384&done,10,1
|
||||
13,file_size:le:65536&done,11,1
|
||||
14,file_size:le:262144&done,12,1
|
||||
15,file_size:le:1048576&done,13,1
|
||||
16,file_size:le:4194304&done,14,1
|
||||
17,file_size:le:16777216&done,15,1
|
||||
18,file_size:le:67108864&done,16,1
|
||||
19,file_size:le:268435456&done,17,1
|
||||
20,file_size:le:1073741824&done,18,1
|
||||
21,file_size:gt:1073741824&done,19,1
|
||||
22,direct&done,20,1
|
||||
|
56
utils/open-cas-loader
Executable file
56
utils/open-cas-loader
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python2
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import subprocess
|
||||
import time
|
||||
import opencas
|
||||
import sys
|
||||
import os
|
||||
import syslog as sl
|
||||
|
||||
def wait_for_cas_ctrl():
|
||||
for i in range(30): # timeout 30s
|
||||
if os.path.exists('/dev/cas_ctrl'):
|
||||
return
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
subprocess.call(['/sbin/modprobe', 'cas_cache'])
|
||||
except:
|
||||
sl.syslog(sl.LOG_ERR, 'Unable to probe cas_cache module')
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
config = opencas.cas_config.from_file('/etc/opencas/opencas.conf',
|
||||
allow_incomplete=True)
|
||||
except Exception as e:
|
||||
sl.syslog(sl.LOG_ERR,
|
||||
'Unable to load opencas config. Reason: {0}'.format(str(e)))
|
||||
exit(1)
|
||||
|
||||
for cache in config.caches.values():
|
||||
if sys.argv[1] == os.path.realpath(cache.device):
|
||||
try:
|
||||
wait_for_cas_ctrl()
|
||||
opencas.start_cache(cache, True)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
sl.syslog(sl.LOG_WARNING,
|
||||
'Unable to load cache {0} ({1}). Reason: {2}'
|
||||
.format(cache.cache_id, cache.device, e.result.stderr))
|
||||
exit(e.result.exit_code)
|
||||
exit(0)
|
||||
for core in cache.cores.values():
|
||||
if sys.argv[1] == os.path.realpath(core.device):
|
||||
try:
|
||||
wait_for_cas_ctrl()
|
||||
opencas.add_core(core, True)
|
||||
except opencas.casadm.CasadmError as e:
|
||||
sl.syslog(sl.LOG_WARNING,
|
||||
'Unable to attach core {0} from cache {1}. Reason: {2}'
|
||||
.format(core.device, cache.cache_id, e.result.stderr))
|
||||
exit(e.result.exit_code)
|
||||
exit(0)
|
||||
26
utils/open-cas-mount-utility
Executable file
26
utils/open-cas-mount-utility
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
# Find all mount units, cut to remove list-units decorations
|
||||
logger "Open CAS Mount Utility checking for $1 FS label..."
|
||||
MOUNT_UNITS=`systemctl --plain list-units | grep \.mount | grep -v '\-\.mount' | awk '{print $1}'`
|
||||
|
||||
for unit in $MOUNT_UNITS
|
||||
do
|
||||
# Find BindsTo keyword, pry out FS label from the .device unit name
|
||||
label=`systemctl show $unit | grep BindsTo | sed "s/.*label\-\(.*\)\.device/\1/;tx;d;:x"`
|
||||
if [ "$label" == "" ]; then
|
||||
continue
|
||||
fi
|
||||
label_unescaped=$(systemd-escape -u $(systemd-escape -u $label))
|
||||
if [ "$label_unescaped" == "$1" ]; then
|
||||
# If FS label matches restart unit
|
||||
logger "Open CAS Mount Utility restarting $unit..."
|
||||
systemctl restart $unit &> /dev/null
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
57
utils/open-cas-shutdown
Normal file
57
utils/open-cas-shutdown
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
#
|
||||
# open-cas-shutdown Stops Open CAS
|
||||
#
|
||||
# chkconfig: 235 05 95
|
||||
# description: Open Cache Acceleration Software Shutdown Trigger
|
||||
#
|
||||
# processname: open-cas-shutdown
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: open-cas-shutdown
|
||||
# Required-Start: $local_fs
|
||||
# Required-Stop: $local_fs
|
||||
# Default-Start: 2 3 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Open Cache Acceleration Software Shutdown Trigger
|
||||
# Description: Open Cache Acceleration Software Shutdown Trigger
|
||||
### END INIT INFO
|
||||
|
||||
# Execution flow
|
||||
|
||||
runfile=/var/lock/subsys/open-cas-shutdown
|
||||
|
||||
function umount_cache_volumes()
|
||||
{
|
||||
BLOCK_DEV_PREFIX=/dev/cas
|
||||
INSTANCES=`ls ${BLOCK_DEV_PREFIX}* | egrep [1-9][0-9]*-[1-9][0-9]*`
|
||||
for inst in $INSTANCES ; do
|
||||
# Umount any mounted Open CAS devices first
|
||||
if [[ `cat /etc/mtab | grep $inst | wc -l` -gt 0 ]] ; then
|
||||
umount $inst &> /dev/null
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start|restart|reload)
|
||||
mkdir -p `dirname $runfile`
|
||||
touch $runfile
|
||||
exit 0
|
||||
;;
|
||||
status)
|
||||
exit 0
|
||||
;;
|
||||
stop)
|
||||
umount_cache_volumes
|
||||
/sbin/cas stop
|
||||
rm -f $runfile
|
||||
exit $?
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
esac
|
||||
14
utils/open-cas-shutdown.service
Normal file
14
utils/open-cas-shutdown.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=Open Cache Acceleration Software Shutdown Trigger
|
||||
After=umount.target
|
||||
Before=final.target
|
||||
JobTimeoutSec=604800
|
||||
DefaultDependencies=no
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/sbin/casctl stop
|
||||
TimeoutStopSec=604800
|
||||
|
||||
[Install]
|
||||
WantedBy=final.target
|
||||
10
utils/open-cas.shutdown
Executable file
10
utils/open-cas.shutdown
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
# systemd-shutdown plugin to stop all remaining Open CAS devices
|
||||
|
||||
/usr/bin/echo "Open CAS cleanup handler" > /dev/kmsg
|
||||
/sbin/casctl stop
|
||||
29
utils/opencas.conf
Normal file
29
utils/opencas.conf
Normal file
@@ -0,0 +1,29 @@
|
||||
version=19.3.0
|
||||
# Version tag has to be first line in this file
|
||||
#
|
||||
# Open CAS configuration file - for reference on syntax
|
||||
# of this file please refer to appropriate documentation
|
||||
|
||||
# NOTES:
|
||||
# 1) It is highly recommended to specify cache/core device using path
|
||||
# that is constant across reboots - e.g. disk device links in
|
||||
# /dev/disk/by-id/, preferably those using device WWN if available:
|
||||
# /dev/disk/by-id/wwn-0x123456789abcdef0
|
||||
# Referencing devices via /dev/sd* may result in cache misconfiguration after
|
||||
# system reboot due to change(s) in drive order.
|
||||
|
||||
## Caches configuration section
|
||||
[caches]
|
||||
## Cache ID Cache device Cache mode Extra fields (optional)
|
||||
## Uncomment and edit the below line for cache configuration
|
||||
#1 /dev/disk/by-id/nvme-INTEL_SSDP.. WT
|
||||
|
||||
## Core devices configuration
|
||||
[cores]
|
||||
## Cache ID Core ID Core device
|
||||
## Uncomment and edit the below line for core configuration
|
||||
#1 1 /dev/disk/by-id/wwn-0x123456789abcdef0
|
||||
|
||||
## To specify use of the IO Classification file, place content of the following line in the
|
||||
## Caches configuration section under Extra fields (optional)
|
||||
## ioclass_file=/etc/opencas/ioclass-config.csv
|
||||
61
utils/opencas.conf.5
Normal file
61
utils/opencas.conf.5
Normal file
@@ -0,0 +1,61 @@
|
||||
.TH opencas.conf 5 __CAS_DATE__ v_CAS_VERSION__
|
||||
.SH NAME
|
||||
opencas.conf \- cas configuration file.
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B /etc/opencas/opencas.conf
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright(c) 2012-2019 by the Intel Corporation.
|
||||
|
||||
.SH DESCRIPTION
|
||||
Contains configurations for cache and core devices to run cache on system startup. Constist of following sections:
|
||||
.RS 3
|
||||
.TP
|
||||
\fBversion\fR First line has to be version tag.
|
||||
.TP
|
||||
\fB[caches]\fR Caches configuration. Following columns are required:
|
||||
.RS 5
|
||||
.IP
|
||||
Cache ID <1-16384>
|
||||
.br
|
||||
Cache device <DEVICE>
|
||||
.br
|
||||
Cache mode {wt|wb|wa|pt}
|
||||
.br
|
||||
Extra fields (optional) ioclass_file=<file>,cleaning_policy=<alru,nop>
|
||||
.RE
|
||||
.TP
|
||||
\fB[cores]\fR Cores configuration. Following columns are required:
|
||||
.RS 5
|
||||
.IP
|
||||
Cache ID <1-16384>
|
||||
.br
|
||||
Core ID <0-4095>
|
||||
.br
|
||||
Core device <DEVICE>
|
||||
.br
|
||||
.RE
|
||||
.TP
|
||||
\fBNOTES\fR
|
||||
.RS
|
||||
1) It is highly recommended to specify cache/core device using path that is constant across reboots - e.g. disk device links in /dev/disk/by-id/, preferably those using device WWN if available: /dev/disk/by-id/wwn-0x123456789abcdef0. Referencing devices via /dev/sd* may result in cache misconfiguration after system reboot due to change(s) in drive order.
|
||||
.TP
|
||||
2) To specify use of the IC Classification file, place ioclass_file=path/to/file.csv in caches configuration section under Extra fields (optional)
|
||||
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
/etc/opencas/opencas.conf
|
||||
Contains configurations for cache and core devices to run cache on system startup or via cas tool.
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Patches and issues may be submitted to the official repository at
|
||||
\fBhttps://open-cas.github.io\fR
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.TP
|
||||
casctl(8), casadm(8)
|
||||
|
||||
678
utils/opencas.py
Normal file
678
utils/opencas.py
Normal file
@@ -0,0 +1,678 @@
|
||||
#!/usr/bin/env python2
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import csv
|
||||
import re
|
||||
import os
|
||||
import stat
|
||||
|
||||
# Casadm functionality
|
||||
|
||||
|
||||
class casadm:
|
||||
casadm_path = '/sbin/casadm'
|
||||
|
||||
class result:
|
||||
def __init__(self, cmd):
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
self.exit_code = p.wait()
|
||||
output = p.communicate()
|
||||
self.stdout = output[0]
|
||||
self.stderr = output[1]
|
||||
|
||||
class CasadmError(Exception):
|
||||
def __init__(self, result):
|
||||
super(casadm.CasadmError, self).__init__('casadm error')
|
||||
self.result = result
|
||||
|
||||
@classmethod
|
||||
def run_cmd(cls, cmd):
|
||||
result = cls.result(cmd)
|
||||
if result.exit_code != 0:
|
||||
raise cls.CasadmError(result)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_version(cls):
|
||||
cmd = [cls.casadm_path,
|
||||
'--version',
|
||||
'--output-format', 'csv']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def list_caches(cls):
|
||||
cmd = [cls.casadm_path,
|
||||
'--list-caches',
|
||||
'--output-format', 'csv']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def check_cache_device(cls, device):
|
||||
cmd = [cls.casadm_path,
|
||||
'--script',
|
||||
'--check-cache-device',
|
||||
'--cache-device', device]
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def start_cache(cls, device, cache_id=None, cache_mode=None,
|
||||
cache_line_size=None, load=False, force=False):
|
||||
cmd = [cls.casadm_path,
|
||||
'--start-cache',
|
||||
'--cache-device', device]
|
||||
if cache_id:
|
||||
cmd += ['--cache-id', str(cache_id)]
|
||||
if cache_mode:
|
||||
cmd += ['--cache-mode', cache_mode]
|
||||
if cache_line_size:
|
||||
cmd += ['--cache-line-size', str(cache_line_size)]
|
||||
if load:
|
||||
cmd += ['--load']
|
||||
if force:
|
||||
cmd += ['--force']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def add_core(cls, device, cache_id, core_id=None, try_add=False):
|
||||
cmd = [cls.casadm_path,
|
||||
'--script',
|
||||
'--add-core',
|
||||
'--core-device', device,
|
||||
'--cache-id', str(cache_id)]
|
||||
if core_id is not None:
|
||||
cmd += ['--core-id', str(core_id)]
|
||||
if try_add:
|
||||
cmd += ['--try-add']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def stop_cache(cls, cache_id, no_flush=False):
|
||||
cmd = [cls.casadm_path,
|
||||
'--stop-cache',
|
||||
'--cache-id', str(cache_id)]
|
||||
if no_flush:
|
||||
cmd += ['--no-data-flush']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def remove_core(cls, cache_id, core_id, detach=False, force=False):
|
||||
cmd = [cls.casadm_path,
|
||||
'--script',
|
||||
'--remove-core',
|
||||
'--cache-id', str(cache_id),
|
||||
'--core-id', str(core_id)]
|
||||
if detach:
|
||||
cmd += ['--detach']
|
||||
if force:
|
||||
cmd += ['--no-flush']
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def set_param(cls, namespace, cache_id, **kwargs):
|
||||
cmd = [cls.casadm_path,
|
||||
'--set-param', '--name', namespace,
|
||||
'--cache-id', str(cache_id)]
|
||||
|
||||
for param, value in kwargs.items():
|
||||
cmd += ['--'+param.replace('_', '-'), str(value)]
|
||||
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def get_params(cls, namespace, cache_id, **kwargs):
|
||||
cmd = [cls.casadm_path,
|
||||
'--get-param', '--name', namespace,
|
||||
'--cache-id', str(cache_id)]
|
||||
|
||||
for param, value in kwargs.items():
|
||||
cmd += ['--'+param.replace('_', '-'), str(value)]
|
||||
|
||||
cmd += ['-o', 'csv']
|
||||
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def flush_parameters(cls, cache_id, policy_type):
|
||||
cmd = [cls.casadm_path,
|
||||
'--flush-parameters',
|
||||
'--cache-id', str(cache_id),
|
||||
'--cleaning-policy-type', policy_type]
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
@classmethod
|
||||
def io_class_load_config(cls, cache_id, ioclass_file):
|
||||
cmd = [cls.casadm_path,
|
||||
'--io-class',
|
||||
'--load-config',
|
||||
'--cache-id', str(cache_id),
|
||||
'--file', ioclass_file]
|
||||
return cls.run_cmd(cmd)
|
||||
|
||||
# Configuration file parser
|
||||
|
||||
|
||||
class cas_config(object):
|
||||
default_location = '/etc/opencas/opencas.conf'
|
||||
|
||||
class ConflictingConfigException(ValueError):
|
||||
pass
|
||||
|
||||
class AlreadyConfiguredException(ValueError):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_by_id_path(path):
|
||||
for id_path in os.listdir('/dev/disk/by-id'):
|
||||
full_path = '/dev/disk/by-id/{0}'.format(id_path)
|
||||
if os.path.realpath(full_path) == os.path.realpath(path):
|
||||
return full_path
|
||||
|
||||
raise ValueError('By-id device link not found for {0}'.format(path))
|
||||
|
||||
@staticmethod
|
||||
def check_block_device(path):
|
||||
if not os.path.exists(path) and path.startswith('/dev/cas'):
|
||||
return
|
||||
|
||||
try:
|
||||
mode = os.stat(path).st_mode
|
||||
except:
|
||||
raise ValueError('{0} not found'.format(path))
|
||||
|
||||
if not stat.S_ISBLK(mode):
|
||||
raise ValueError('{0} is not block device'.format(path))
|
||||
|
||||
class cache_config(object):
|
||||
def __init__(self, cache_id, device, cache_mode, **params):
|
||||
self.cache_id = int(cache_id)
|
||||
self.device = device
|
||||
self.cache_mode = cache_mode
|
||||
self.params = params
|
||||
self.cores = dict()
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line, allow_incomplete=False):
|
||||
values = line.split()
|
||||
if len(values) < 3:
|
||||
raise ValueError('Invalid cache configuration (too few columns)')
|
||||
elif len(values) > 4:
|
||||
raise ValueError('Invalid cache configuration (too many columns)')
|
||||
|
||||
cache_id = int(values[0])
|
||||
device = values[1]
|
||||
cache_mode = values[2].lower()
|
||||
|
||||
params = dict()
|
||||
if len(values) > 3:
|
||||
for param in values[3].split(','):
|
||||
param_name, param_value = param.split('=')
|
||||
if param_name in params:
|
||||
raise ValueError('Invalid cache configuration (repeated parameter')
|
||||
params[param_name] = param_value
|
||||
|
||||
cache_config = cls(cache_id, device, cache_mode, **params)
|
||||
cache_config.validate_config(False, allow_incomplete)
|
||||
|
||||
return cache_config
|
||||
|
||||
def validate_config(self, force, allow_incomplete=False):
|
||||
type(self).check_cache_id_valid(self.cache_id)
|
||||
self.check_recursive()
|
||||
self.check_cache_mode_valid(self.cache_mode)
|
||||
for param_name, param_value in self.params.iteritems():
|
||||
self.validate_parameter(param_name, param_value)
|
||||
|
||||
if not allow_incomplete:
|
||||
cas_config.check_block_device(self.device)
|
||||
if not force:
|
||||
self.check_cache_device_empty()
|
||||
|
||||
def validate_parameter(self, param_name, param_value):
|
||||
if param_name == 'ioclass_file':
|
||||
if not os.path.exists(param_value):
|
||||
raise ValueError('Incorrect path to io_class file')
|
||||
elif param_name == 'cleaning_policy':
|
||||
self.check_cleaning_policy_valid(param_value)
|
||||
elif param_name == 'cache_line_size':
|
||||
self.check_cache_line_size_valid(param_value)
|
||||
else:
|
||||
raise ValueError('{0} is unknown parameter name'.format(param_name))
|
||||
|
||||
@staticmethod
|
||||
def check_cache_id_valid(cache_id):
|
||||
if not 1 <= int(cache_id) <= 16384:
|
||||
raise ValueError('{0} is invalid cache id'.format(cache_id))
|
||||
|
||||
def check_cache_device_empty(self):
|
||||
try:
|
||||
result = casadm.run_cmd(['lsblk', '-o', 'NAME', '-l', '-n', self.device])
|
||||
except:
|
||||
# lsblk returns non-0 if it can't probe for partitions
|
||||
# this means that we're probably dealing with atomic device
|
||||
# let it through
|
||||
return
|
||||
|
||||
if len(list(filter(lambda a: a != '', result.stdout.split('\n')))) > 1:
|
||||
raise ValueError(
|
||||
'Partitions found on device {0}. Use force option to ignore'.
|
||||
format(self.device))
|
||||
|
||||
def check_cache_mode_valid(self, cache_mode):
|
||||
if cache_mode.lower() not in ['wt', 'pt', 'wa', 'wb']:
|
||||
raise ValueError('Invalid cache mode {0}'.format(cache_mode))
|
||||
|
||||
def check_cleaning_policy_valid(self, cleaning_policy):
|
||||
if cleaning_policy.lower() not in ['acp', 'alru', 'nop']:
|
||||
raise ValueError('{0} is invalid cleaning policy name'.format(
|
||||
cleaning_policy))
|
||||
|
||||
def check_cache_line_size_valid(self, cache_line_size):
|
||||
if cache_line_size not in ['4', '8', '16', '32', '64']:
|
||||
raise ValueError('{0} is invalid cache line size'.format(
|
||||
cache_line_size))
|
||||
|
||||
def check_recursive(self):
|
||||
if not self.device.startswith('/dev/cas'):
|
||||
return
|
||||
|
||||
ids = self.device.split('/dev/cas')[1]
|
||||
device_cache_id, _ = ids.split('-')
|
||||
|
||||
if int(device_cache_id) == self.cache_id:
|
||||
raise ValueError('Recursive configuration detected')
|
||||
|
||||
def to_line(self):
|
||||
ret = '{0}\t{1}\t{2}'.format(self.cache_id, self.device, self.cache_mode)
|
||||
if len(self.params) > 0:
|
||||
i = 0
|
||||
for param, value in self.params.iteritems():
|
||||
if i > 0:
|
||||
ret += ','
|
||||
else:
|
||||
ret += '\t'
|
||||
|
||||
ret += '{0}={1}'.format(param, value)
|
||||
i += 1
|
||||
ret += '\n'
|
||||
|
||||
return ret
|
||||
|
||||
class core_config(object):
|
||||
def __init__(self, cache_id, core_id, path):
|
||||
self.cache_id = int(cache_id)
|
||||
self.core_id = int(core_id)
|
||||
self.device = path
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line, allow_incomplete=False):
|
||||
values = line.split()
|
||||
if len(values) > 3:
|
||||
raise ValueError('Invalid core configuration (too many columns)')
|
||||
elif len(values) < 3:
|
||||
raise ValueError('Invalid core configuration (too few columns)')
|
||||
|
||||
cache_id = int(values[0])
|
||||
core_id = int(values[1])
|
||||
device = values[2]
|
||||
|
||||
core_config = cls(cache_id, core_id, device)
|
||||
|
||||
core_config.validate_config(allow_incomplete)
|
||||
|
||||
return core_config
|
||||
|
||||
def validate_config(self, allow_incomplete=False):
|
||||
self.check_core_id_valid()
|
||||
self.check_recursive()
|
||||
cas_config.cache_config.check_cache_id_valid(self.cache_id)
|
||||
if not allow_incomplete:
|
||||
cas_config.check_block_device(self.device)
|
||||
|
||||
def check_core_id_valid(self):
|
||||
if not 0 <= int(self.core_id) <= 4095:
|
||||
raise ValueError('{0} is invalid core id'.format(self.core_id))
|
||||
|
||||
def check_recursive(self):
|
||||
if not self.device.startswith('/dev/cas'):
|
||||
return
|
||||
|
||||
ids = self.device.split('/dev/cas')[1]
|
||||
device_cache_id, _ = ids.split('-')
|
||||
|
||||
if int(device_cache_id) == self.cache_id:
|
||||
raise ValueError('Recursive configuration detected')
|
||||
|
||||
def to_line(self):
|
||||
return '{0}\t{1}\t{2}\n'.format(self.cache_id, self.core_id, self.device)
|
||||
|
||||
def __init__(self, caches=None, cores=None, version_tag=None):
|
||||
self.caches = caches if caches else dict()
|
||||
|
||||
self.cores = cores if cores else list()
|
||||
|
||||
self.version_tag = version_tag
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, config_file, allow_incomplete=False):
|
||||
section_caches = False
|
||||
section_cores = False
|
||||
|
||||
try:
|
||||
with open(config_file, 'r') as conf:
|
||||
version_tag = conf.readline()
|
||||
if not re.findall(r'^version=.*$', version_tag):
|
||||
raise ValueError('No version tag found!')
|
||||
|
||||
config = cls(version_tag=version_tag)
|
||||
|
||||
for line in conf:
|
||||
line = line.split('#')[0].rstrip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if line == '[caches]':
|
||||
section_caches = True
|
||||
continue
|
||||
|
||||
if line == '[cores]':
|
||||
section_caches = False
|
||||
section_cores = True
|
||||
continue
|
||||
|
||||
if section_caches:
|
||||
cache = cas_config.cache_config.from_line(line, allow_incomplete)
|
||||
config.insert_cache(cache)
|
||||
elif section_cores:
|
||||
core = cas_config.core_config.from_line(line, allow_incomplete)
|
||||
config.insert_core(core)
|
||||
except ValueError:
|
||||
raise
|
||||
except IOError:
|
||||
raise Exception('Couldn\'t open config file')
|
||||
except:
|
||||
raise
|
||||
|
||||
return config
|
||||
|
||||
def insert_cache(self, new_cache_config):
|
||||
if new_cache_config.cache_id in self.caches:
|
||||
if (os.path.realpath(self.caches[new_cache_config.cache_id].device)
|
||||
!= os.path.realpath(new_cache_config.device)):
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'Other cache device configured under this id')
|
||||
else:
|
||||
raise cas_config.AlreadyConfiguredException(
|
||||
'Cache already configured')
|
||||
|
||||
for cache_id, cache in self.caches.iteritems():
|
||||
if cache_id != new_cache_config.cache_id:
|
||||
if (os.path.realpath(new_cache_config.device)
|
||||
== os.path.realpath(cache.device)):
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'This cache device is already configured as a cache')
|
||||
|
||||
for _, core in cache.cores.iteritems():
|
||||
if (os.path.realpath(core.device)
|
||||
== os.path.realpath(new_cache_config.device)):
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'This cache device is already configured as a core')
|
||||
|
||||
try:
|
||||
new_cache_config.device = cas_config.get_by_id_path(new_cache_config.device)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.caches[new_cache_config.cache_id] = new_cache_config
|
||||
|
||||
def insert_core(self, new_core_config):
|
||||
if new_core_config.cache_id not in self.caches:
|
||||
raise KeyError('Cache id {0} doesn\'t exist'.format(new_core_config.cache_id))
|
||||
|
||||
try:
|
||||
for cache_id, cache in self.caches.iteritems():
|
||||
if (os.path.realpath(cache.device)
|
||||
== os.path.realpath(new_core_config.device)):
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'Core device already configured as a cache')
|
||||
|
||||
for core_id, core in cache.cores.iteritems():
|
||||
if (cache_id == new_core_config.cache_id
|
||||
and core_id == new_core_config.core_id):
|
||||
if (os.path.realpath(core.device)
|
||||
== os.path.realpath(new_core_config.device)):
|
||||
raise cas_config.AlreadyConfiguredException(
|
||||
'Core already configured')
|
||||
else:
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'Other core device configured under this id')
|
||||
else:
|
||||
if (os.path.realpath(core.device)
|
||||
== os.path.realpath(new_core_config.device)):
|
||||
raise cas_config.ConflictingConfigException(
|
||||
'This core device is already configured as a core')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
new_core_config.device = cas_config.get_by_id_path(new_core_config.device)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.caches[new_core_config.cache_id].cores[new_core_config.core_id] = new_core_config
|
||||
self.cores += [new_core_config]
|
||||
|
||||
def is_empty(self):
|
||||
if len(self.caches) > 0 or len(self.cores) > 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def write(self, config_file):
|
||||
try:
|
||||
with open(config_file, 'w') as conf:
|
||||
conf.write('{0}\n'.format(self.version_tag))
|
||||
conf.write('# This config was automatically generated\n')
|
||||
|
||||
conf.write('[caches]\n')
|
||||
for _, cache in self.caches.iteritems():
|
||||
conf.write(cache.to_line())
|
||||
|
||||
conf.write('\n[cores]\n')
|
||||
for core in self.cores:
|
||||
conf.write(core.to_line())
|
||||
|
||||
except:
|
||||
raise Exception('Couldn\'t write config file')
|
||||
|
||||
# Config helper functions
|
||||
|
||||
|
||||
def start_cache(cache, load, force=False):
|
||||
casadm.start_cache(
|
||||
device=cache.device,
|
||||
cache_id=cache.cache_id,
|
||||
cache_mode=cache.cache_mode,
|
||||
cache_line_size=cache.params.get('cache_line_size'),
|
||||
load=load,
|
||||
force=force)
|
||||
|
||||
def configure_cache(cache):
|
||||
if cache.params.has_key('cleaning_policy'):
|
||||
casadm.set_param('cleaning', cache_id=cache.cache_id,
|
||||
policy=cache.params['cleaning_policy'])
|
||||
if cache.params.has_key('ioclass_file'):
|
||||
casadm.io_class_load_config(cache_id=cache.cache_id,
|
||||
ioclass_file=cache.params['ioclass_file'])
|
||||
|
||||
def add_core(core, attach):
|
||||
casadm.add_core(
|
||||
device=core.device,
|
||||
cache_id=core.cache_id,
|
||||
core_id=core.core_id,
|
||||
try_add=attach)
|
||||
|
||||
# Another helper functions
|
||||
|
||||
def is_cache_started(cache_config):
|
||||
dev_list = get_caches_list()
|
||||
for dev in dev_list:
|
||||
if dev['type'] == 'cache' and int(dev['id']) == cache_config.cache_id:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_core_added(core_config):
|
||||
dev_list = get_caches_list()
|
||||
cache_id = 0
|
||||
for dev in dev_list:
|
||||
if dev['type'] == 'cache':
|
||||
cache_id = int(dev['id'])
|
||||
|
||||
if (dev['type'] == 'core' and
|
||||
cache_id == core_config.cache_id and
|
||||
int(dev['id']) == core_config.core_id):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_caches_list():
|
||||
result = casadm.list_caches()
|
||||
return list(csv.DictReader(result.stdout.split('\n')))
|
||||
|
||||
def check_cache_device(device):
|
||||
result = casadm.check_cache_device(device)
|
||||
return list(csv.DictReader(result.stdout.split('\n')))[0]
|
||||
|
||||
def get_cas_version():
|
||||
version = casadm.get_version()
|
||||
|
||||
ret = {}
|
||||
for line in version.stdout.split('\n')[1:]:
|
||||
try:
|
||||
component, version = line.split(',')
|
||||
except:
|
||||
continue
|
||||
ret[component] = version
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class CompoundException(Exception):
|
||||
def __init__(self):
|
||||
super(CompoundException, self).__init__()
|
||||
self.exception_list = list()
|
||||
|
||||
def __str__(self):
|
||||
s = "Multiple exceptions occured:\n" if len(self.exception_list) > 1 else ""
|
||||
|
||||
for e in self.exception_list:
|
||||
s += '{0}\n'.format(str(e))
|
||||
|
||||
return s
|
||||
|
||||
def add_exception(self, e):
|
||||
if type(e) is CompoundException:
|
||||
self.exception_list += e.exception_list
|
||||
else:
|
||||
self.exception_list += [e]
|
||||
|
||||
def is_empty(self):
|
||||
return len(self.exception_list) == 0
|
||||
|
||||
def raise_nonempty(self):
|
||||
if self.is_empty():
|
||||
return
|
||||
else:
|
||||
raise self
|
||||
|
||||
def detach_core_recursive(cache_id, core_id, flush):
|
||||
# Catching exceptions is left to uppermost caller of detach_core_recursive
|
||||
# as the immediate caller that made a recursive call depends on the callee
|
||||
# to remove core and thus release reference to lower level cache volume.
|
||||
l_cache_id = ''
|
||||
for dev in get_caches_list():
|
||||
if dev['type'] == 'cache':
|
||||
l_cache_id = dev['id']
|
||||
elif dev['type'] == 'core' and dev['status'] == 'Active':
|
||||
if '/dev/cas{0}-{1}'.format(cache_id, core_id) in dev['disk']:
|
||||
detach_core_recursive(l_cache_id, dev['id'], flush)
|
||||
elif l_cache_id == cache_id and dev['id'] == core_id and dev['status'] != 'Active':
|
||||
return
|
||||
|
||||
casadm.remove_core(cache_id, core_id, detach = True, force = not flush)
|
||||
|
||||
def detach_all_cores(flush):
|
||||
error = CompoundException()
|
||||
|
||||
try:
|
||||
dev_list = get_caches_list()
|
||||
except casadm.CasadmError as e:
|
||||
raise Exception('Unable to list caches. Reason:\n{0}'.format(
|
||||
e.result.stderr))
|
||||
except:
|
||||
raise Exception('Unable to list caches.')
|
||||
|
||||
for dev in dev_list:
|
||||
if dev['type'] == 'cache':
|
||||
cache_id = dev['id']
|
||||
elif dev['type'] == 'core' and dev['status'] == "Active":
|
||||
# In case of exception we proceed with detaching remaining core instances
|
||||
# to gracefully shutdown as many cache instances as possible.
|
||||
try:
|
||||
detach_core_recursive(cache_id, dev['id'], flush)
|
||||
except casadm.CasadmError as e:
|
||||
error.add_exception(Exception(
|
||||
'Unable to detach core {0}. Reason:\n{1}'.format(
|
||||
dev['disk'], e.result.stderr)))
|
||||
except:
|
||||
error.add_exception(Exception(
|
||||
'Unable to detach core {0}.'.format(dev['disk'])))
|
||||
|
||||
error.raise_nonempty()
|
||||
|
||||
def stop_all_caches(flush):
|
||||
error = CompoundException()
|
||||
|
||||
try:
|
||||
dev_list = get_caches_list()
|
||||
except casadm.CasadmError as e:
|
||||
raise Exception('Unable to list caches. Reason:\n{0}'.format(
|
||||
e.result.stderr))
|
||||
except:
|
||||
raise Exception('Unable to list caches.')
|
||||
|
||||
for dev in dev_list:
|
||||
if dev['type'] == 'cache':
|
||||
# In case of exception we proceed with stopping subsequent cache instances
|
||||
# to gracefully shutdown as many cache instances as possible.
|
||||
try:
|
||||
casadm.stop_cache(dev['id'], not flush)
|
||||
except casadm.CasadmError as e:
|
||||
error.add_exception(Exception(
|
||||
'Unable to stop cache {0}. Reason:\n{1}'.format(
|
||||
dev['disk'], e.result.stderr)))
|
||||
except:
|
||||
error.add_exception(Exception(
|
||||
'Unable to stop cache {0}.'.format(dev['disk'])))
|
||||
|
||||
error.raise_nonempty()
|
||||
|
||||
def stop(flush):
|
||||
error = CompoundException()
|
||||
|
||||
try:
|
||||
detach_all_cores(flush)
|
||||
except Exception as e:
|
||||
error.add_exception(e)
|
||||
|
||||
try:
|
||||
stop_all_caches(False)
|
||||
except Exception as e:
|
||||
error.add_exception(e)
|
||||
|
||||
error.raise_nonempty()
|
||||
|
||||
Reference in New Issue
Block a user