Skip to content

Commit

Permalink
More generic code + MVCLI support
Browse files Browse the repository at this point in the history
  • Loading branch information
ulmitov committed May 10, 2023
1 parent 4f6af49 commit faca59f
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 183 deletions.
85 changes: 24 additions & 61 deletions pyarcconf/arcconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,16 @@
https://wiki.miko.ru/kb:sysadm:arcconf
"""
import logging
import subprocess
from . import runner

from . import parser

logger = logging.getLogger('pyArcconf')


class Arcconf():
class Arcconf(runner.CMDRunner):
"""Arcconf wrapper class."""
def __init__(self, cmdrunner=None):
self.runner = cmdrunner or runner.CMDRunner()

def __init__(self, path=''):
"""Initialize a new Arcconf object.
Args:
path (str): path to arcconf binary
"""
self.path = path or self._exec(['which', 'arcconf'])[0].split('\n')[0]

def _exec(self, cmd):
"""Execute a command using arcconf.
Args:
cmd (list):
Returns:
str: arcconf output
Raises:
RuntimeError: if command fails
"""
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
_stdout, _stderr = proc.communicate()
if isinstance(_stdout, bytes):
_stdout = _stdout.decode('utf8').strip()
_stderr = _stderr.decode('utf8').strip()
return _stdout, proc.returncode

def _execute(self, cmd, args=[]):
"""Execute a command using arcconf.
def _execute(self, cmd, args=None):
"""Execute a command
Return codes:
0x00: SUCCESS
0x01: FAILURE - The requested command failed
Expand All @@ -61,28 +32,20 @@ def _execute(self, cmd, args=[]):
Args:
args (list):
Returns:
str: arcconf output
str: output
Raises:
RuntimeError: if command fails
"""
args = args or []
if type(cmd) == str:
cmd = [cmd]
out, rc = self._exec([self.path] + cmd + args)
out, err, rc = self.runner.run(args=[self.runner.path] + cmd + args, universal_newlines=True)
for arg in cmd + args:
if '>' in arg:
# out was redirected
return out, rc
out = out.split('\n')
while out and not out[-1]:
# remove empty lines in the end of out
del out[-1]
if 'Command completed successfully' not in out[-1]:
logger.error(f'{cmd} {out[-1]}')
del out[-1]
while out and not out[-1]:
# remove empty lines in the end of out
del out[-1]
return '\n'.join(out), rc
out = runner.sanitize_stdout(out, 'Command ')
return out, rc

def get_version(self):
"""Check the versions of all connected controllers.
Expand All @@ -92,7 +55,7 @@ def get_version(self):
"""
versions = {}
result = self._execute('GETVERSION')[0]
result = parser.cut_lines(result, 1)
result = runner.cut_lines(result, 1)
for part in result.split('\n\n'):
lines = part.split('\n')
id_ = lines[0].split('#')[1]
Expand All @@ -104,26 +67,26 @@ def get_version(self):
return versions

def list(self):
"""List all adapter by their ids.
"""List all controllers by their ids.
Returns:
list: list of adapter ids
list: list of controller ids
"""
adapters = []
controllers = []
result = self._execute('LIST')[0]
result = parser.cut_lines(result, 6)
result = runner.cut_lines(result, 6)
for line in list(filter(None, result.split('\n'))):
adapters.append(line.split(':')[0].strip().split()[1])
return adapters
controllers.append(line.split(':')[0].strip().split()[1])
return controllers

def get_adapters(self):
"""Get all adapter objects for further interaction.
def get_controllers(self):
"""Get all controller objects for further interaction.
Returns:
list: list of adapter objects.
list: list of controller objects.
"""
from common.pyarcconf.controller import Controller
adapters = []
controllers = []
for idx in self.list():
adapters.append(Controller(idx, self))
return adapters
controllers.append(Controller(idx, self))
return controllers
50 changes: 25 additions & 25 deletions pyarcconf/array.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
"""Pyarcconf submodule, which provides a logical drive representing class."""
"""Logical (Virtual) array class"""

from . import parser
from . import runner
from .arcconf import Arcconf
from .physical_drive import PhysicalDrive


class Array():
"""Object which represents an Array of logical\physical drives"""

def __init__(self, adapter_obj, id_, arcconf=None):
def __init__(self, controller_obj, id_, cmdrunner=None):
"""Initialize a new LogicalDrive object."""
self.arcconf = arcconf or Arcconf()
self.adapter = adapter_obj
self.adapter_id = str(adapter_obj.id)
self.runner = cmdrunner or Arcconf()
self.controller = controller_obj
self.controller_id = str(controller_obj.id)
self.id = str(id_)

# pystorcli compliance
self.facts = {}

def _execute(self, cmd, args=[]):
"""Execute a command using arcconf.
"""Execute a command
Args:
args (list):
Returns:
str: arcconf output
str: output
Raises:
RuntimeError: if command fails
"""
if cmd == 'GETCONFIG':
base_cmd = [cmd, self.adapter_id]
base_cmd = [cmd, self.controller_id]
else:
base_cmd = [cmd, self.adapter_id, 'ARRAY', self.id_]
return self.arcconf._execute(base_cmd + args)
base_cmd = [cmd, self.controller_id, 'ARRAY', self.id_]
return self.runner._execute(base_cmd + args)

def __repr__(self):
"""Define a basic representation of the class object."""
Expand All @@ -46,31 +46,31 @@ def update(self, config=''):
if config and type(config) == list:
config = '\n'.join(config)
config = config or self._get_config()
config = config.split(parser.SEPARATOR_SECTION)[0]
config = config.split(runner.SEPARATOR_SECTION)[0]
for line in config.split('\n'):
if parser.SEPARATOR_ATTRIBUTE in line:
key, value = parser.convert_property(line)
if runner.SEPARATOR_ATTRIBUTE in line:
key, value = runner.convert_property(line)
self.__setattr__(key, value)
# pystorcli compliance
key = parser.convert_key_dict(line)
key = runner.convert_key_dict(line)
self.facts[key] = value

def _get_config(self):
result = self._execute('GETCONFIG', ['AR', self.id])[0]
result = parser.cut_lines(result, 4)
result = runner.cut_lines(result, 4)
return result

@property
def drives(self):
config = self._get_config()
config = config.split(parser.SEPARATOR_SECTION)[-1]
config = config.split(runner.SEPARATOR_SECTION)[-1]
drives = []
for line in config.split('\n'):
if not line:
continue
serial = line.split(')')[1].strip()
# TODO: create new objects instead of getting them from the controller ?
for d in self.adapter.drives:
for d in self.controller.drives:
if serial == d.serial:
d.update()
drives.append(d)
Expand All @@ -79,12 +79,12 @@ def drives(self):
@property
def lgs(self):
config = self._get_config()
config = config.split(parser.SEPARATOR_SECTION)[-4]
config = config.split(runner.SEPARATOR_SECTION)[-4]
drives = []
for line in config.split('\n'):
serial = line.split(parser.SEPARATOR_ATTRIBUTE)[0].strip()
serial = line.split(runner.SEPARATOR_ATTRIBUTE)[0].strip()
# TODO: create new objects instead of getting them from the controller ?
for d in self.adapter.drives:
for d in self.controller.drives:
if serial == d.serial:
d.update()
drives.append(d)
Expand All @@ -101,7 +101,7 @@ def set_name(self, name):
result, rc = self._execute('SETNAME', [name])
if not rc:
result = self._execute('GETCONFIG', ['LD', self.id])
result = parser.cut_lines(result, 4)
result = runner.cut_lines(result, 4)
for line in result.split('\n'):
if line.strip().startswith('Logical Device Name'):
self.logical_device_name = line.split(':')[1].strip().lower()
Expand All @@ -119,7 +119,7 @@ def set_state(self, state='OPTIMAL'):
result, rc = self._execute('SETSTATE', [state])
if not rc:
result = self._execute('GETCONFIG', ['LD', self.id])
result = parser.cut_lines(result, 4)
result = runner.cut_lines(result, 4)
for line in result.split('\n'):
if line.strip().startswith('Status'):
self.status_of_logical_device = line.split(':')[1].strip().lower()
Expand All @@ -144,10 +144,10 @@ def set_cache(self, mode):
result, rc = self._execute('SETCACHE', [mode])
if not rc:
result = self._execute('GETCONFIG', ['LD', self.id])
result = parser.cut_lines(result, 4)
result = runner.cut_lines(result, 4)
for line in result.split('\n'):
if line.split(':')[0].strip() in ['Read-cache', 'Write-cache']:
key, value = parser.convert_property(line)
key, value = runner.convert_property(line)
self.__setattr__(key, value)
return True
return False
Expand Down
Loading

0 comments on commit faca59f

Please sign in to comment.