# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from .automatedproperties import AutomatedProperties

from . import utils
from .utils import pv_obj_path_generate, vg_obj_path_generate, n
import dbus
from . import cfg
from .cfg import VG_INTERFACE
from . import cmdhandler
from .request import RequestEntry
from .loader import common
from .state import State
from . import background
from .utils import round_size


# noinspection PyUnusedLocal
def vgs_state_retrieve(selection, cache_refresh=True):
	rc = []

	if cache_refresh:
		cfg.db.refresh()

	for v in cfg.db.fetch_vgs(selection):
		rc.append(
			VgState(
				v['vg_uuid'], v['vg_name'], v['vg_fmt'], n(v['vg_size']),
				n(v['vg_free']), v['vg_sysid'], n(v['vg_extent_size']),
				n(v['vg_extent_count']), n(v['vg_free_count']),
				v['vg_profile'], n(v['max_lv']), n(v['max_pv']),
				n(v['pv_count']), n(v['lv_count']), n(v['snap_count']),
				n(v['vg_seqno']), n(v['vg_mda_count']),
				n(v['vg_mda_free']), n(v['vg_mda_size']),
				n(v['vg_mda_used_count']), v['vg_attr'], v['vg_tags']))
	return rc


def load_vgs(vg_specific=None, object_path=None, refresh=False,
		emit_signal=False, cache_refresh=True):
	return common(vgs_state_retrieve, (Vg,), vg_specific, object_path, refresh,
					emit_signal, cache_refresh)


# noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal
class VgState(State):
	@property
	def lvm_id(self):
		return self.Name

	def identifiers(self):
		return (self.Uuid, self.Name)

	def _lv_paths_build(self):
		rc = []
		for lv in cfg.db.lvs_in_vg(self.Uuid):
			(lv_name, meta, lv_uuid) = lv
			full_name = "%s/%s" % (self.Name, lv_name)

			gen = utils.lv_object_path_method(lv_name, meta)

			lv_path = cfg.om.get_object_path_by_lvm_id(
				lv_uuid, full_name, gen)
			rc.append(lv_path)
		return dbus.Array(rc, signature='o')

	def _pv_paths_build(self):
		rc = []
		for p in cfg.db.pvs_in_vg(self.Uuid):
			(pv_name, pv_uuid) = p
			rc.append(cfg.om.get_object_path_by_lvm_id(
				pv_uuid, pv_name, pv_obj_path_generate))
		return dbus.Array(rc, signature='o')

	def __init__(self, Uuid, Name, Fmt,
			SizeBytes, FreeBytes, SysId, ExtentSizeBytes,
			ExtentCount, FreeCount, Profile, MaxLv, MaxPv, PvCount,
			LvCount, SnapCount, Seqno, MdaCount, MdaFree,
			MdaSizeBytes, MdaUsedCount, attr, tags):
		utils.init_class_from_arguments(self)
		self.Pvs = self._pv_paths_build()
		self.Lvs = self._lv_paths_build()

	def create_dbus_object(self, path):
		if not path:
			path = cfg.om.get_object_path_by_lvm_id(
				self.Uuid, self.Name, vg_obj_path_generate)
		return Vg(path, self)

	# noinspection PyMethodMayBeStatic
	def creation_signature(self):
		return (Vg, vg_obj_path_generate)


# noinspection PyPep8Naming
@utils.dbus_property(VG_INTERFACE, 'Uuid', 's')
@utils.dbus_property(VG_INTERFACE, 'Name', 's')
@utils.dbus_property(VG_INTERFACE, 'Fmt', 's')
@utils.dbus_property(VG_INTERFACE, 'SizeBytes', 't', 0)
@utils.dbus_property(VG_INTERFACE, 'FreeBytes', 't', 0)
@utils.dbus_property(VG_INTERFACE, 'SysId', 's')
@utils.dbus_property(VG_INTERFACE, 'ExtentSizeBytes', 't')
@utils.dbus_property(VG_INTERFACE, 'ExtentCount', 't')
@utils.dbus_property(VG_INTERFACE, 'FreeCount', 't')
@utils.dbus_property(VG_INTERFACE, 'Profile', 's')
@utils.dbus_property(VG_INTERFACE, 'MaxLv', 't')
@utils.dbus_property(VG_INTERFACE, 'MaxPv', 't')
@utils.dbus_property(VG_INTERFACE, 'PvCount', 't')
@utils.dbus_property(VG_INTERFACE, 'LvCount', 't')
@utils.dbus_property(VG_INTERFACE, 'SnapCount', 't')
@utils.dbus_property(VG_INTERFACE, 'Seqno', 't')
@utils.dbus_property(VG_INTERFACE, 'MdaCount', 't')
@utils.dbus_property(VG_INTERFACE, 'MdaFree', 't')
@utils.dbus_property(VG_INTERFACE, 'MdaSizeBytes', 't')
@utils.dbus_property(VG_INTERFACE, 'MdaUsedCount', 't')
class Vg(AutomatedProperties):
	_Tags_meta = ("as", VG_INTERFACE)
	_Pvs_meta = ("ao", VG_INTERFACE)
	_Lvs_meta = ("ao", VG_INTERFACE)
	_Writeable_meta = ("b", VG_INTERFACE)
	_Readable_meta = ("b", VG_INTERFACE)
	_Resizeable_meta = ("b", VG_INTERFACE)
	_Exportable_meta = ('b', VG_INTERFACE)
	_Partial_meta = ('b', VG_INTERFACE)
	_AllocContiguous_meta = ('b', VG_INTERFACE)
	_AllocCling_meta = ('b', VG_INTERFACE)
	_AllocNormal_meta = ('b', VG_INTERFACE)
	_AllocAnywhere_meta = ('b', VG_INTERFACE)
	_Clustered_meta = ('b', VG_INTERFACE)

	# noinspection PyUnusedLocal,PyPep8Naming
	def __init__(self, object_path, object_state):
		super(Vg, self).__init__(object_path, vgs_state_retrieve)
		self.set_interface(VG_INTERFACE)
		self._object_path = object_path
		self.state = object_state

	@staticmethod
	def fetch_new_lv(vg_name, lv_name):
		full_name = "%s/%s" % (vg_name, lv_name)

		cfg.load()
		l = cfg.om.get_object_by_lvm_id(full_name)
		created_lv = l.dbus_object_path()

		return created_lv

	@staticmethod
	def _rename(uuid, vg_name, new_name, rename_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_rename(vg_name, new_name,
												rename_options)
			if rc == 0:
				cfg.load()
			else:
				# Need to work on error handling, need consistent
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))
		return '/'

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='sia{sv}', out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Rename(self, name, tmo, rename_options, cb, cbe):
		utils.validate_vg_name(VG_INTERFACE, name)
		r = RequestEntry(tmo, Vg._rename,
				(self.state.Uuid, self.state.lvm_id, name,
				rename_options), cb, cbe, False)
		cfg.worker_q.put(r)

	@staticmethod
	def _remove(uuid, vg_name, remove_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			# Remove the VG, if successful then remove from the model
			rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)

			if rc == 0:
				# Remove the VG
				cfg.om.remove_object(dbo, True)

				# If an LV has hidden LVs, things can get quite involved,
				# especially if it's the last thin pool to get removed, so
				# lets refresh all
				cfg.load()

			else:
				# Need to work on error handling, need consistent
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))
		return '/'

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='ia{sv}', out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Remove(self, tmo, remove_options, cb, cbe):
		r = RequestEntry(tmo, Vg._remove,
				(self.state.Uuid, self.state.lvm_id, remove_options),
				cb, cbe, False)
		cfg.worker_q.put(r)

	@staticmethod
	def _change(uuid, vg_name, change_options):
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_change(change_options, vg_name)

			# To use an example with d-feet (Method input)
			# {"activate": __import__('gi.repository.GLib', globals(),
			# locals(), ['Variant']).Variant("s", "n")}

			if rc == 0:
				cfg.load()
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))
		return '/'

	# TODO: This should be broken into a number of different methods
	# instead of having one method that takes a hash for parameters.  Some of
	# the changes that vgchange does works on entire system, not just a
	# specfic vg, thus that should be in the Manager interface.
	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='ia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Change(self, tmo, change_options, cb, cbe):
		r = RequestEntry(tmo, Vg._change,
				(self.state.Uuid, self.state.lvm_id, change_options),
				cb, cbe, False)
		cfg.worker_q.put(r)

	@staticmethod
	def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			pv_devices = []

			# If pv_object_paths is not empty, then get the device paths
			if pv_object_paths and len(pv_object_paths) > 0:
				for pv_op in pv_object_paths:
					pv = cfg.om.get_object_by_path(pv_op)
					if pv:
						pv_devices.append(pv.lvm_id)
					else:
						raise dbus.exceptions.DBusException(
							VG_INTERFACE,
							'PV Object path not found = %s!' % pv_op)

			rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices,
												reduce_options)
			if rc == 0:
				cfg.load()
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))
		return '/'

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='baoia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Reduce(self, missing, pv_object_paths, tmo, reduce_options, cb, cbe):
		r = RequestEntry(tmo, Vg._reduce,
				(self.state.Uuid, self.state.lvm_id, missing,
				pv_object_paths, reduce_options), cb, cbe, False)
		cfg.worker_q.put(r)

	@staticmethod
	def _extend(uuid, vg_name, pv_object_paths, extend_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			extend_devices = []

			for i in pv_object_paths:
				pv = cfg.om.get_object_by_path(i)
				if pv:
					extend_devices.append(pv.lvm_id)
				else:
					raise dbus.exceptions.DBusException(
						VG_INTERFACE, 'PV Object path not found = %s!' % i)

			if len(extend_devices):
				rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices,
													extend_options)
				if rc == 0:
					cfg.load()
				else:
					raise dbus.exceptions.DBusException(
						VG_INTERFACE,
						'Exit code %s, stderr = %s' % (str(rc), err))
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE, 'No pv_object_paths provided!')
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))
		return '/'

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='aoia{sv}', out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Extend(self, pv_object_paths, tmo, extend_options, cb, cbe):
		r = RequestEntry(tmo, Vg._extend,
				(self.state.Uuid, self.state.lvm_id, pv_object_paths,
				extend_options),
				cb, cbe, False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='o(tt)a(ott)ia{sv}',
		out_signature='o')
	def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges,
			tmo, move_options):
		return background.move(
			VG_INTERFACE, None, pv_src_obj, pv_source_range,
			pv_dests_and_ranges, move_options, tmo)

	@staticmethod
	def _lv_create(uuid, vg_name, name, size_bytes, pv_dests_and_ranges,
			create_options):
		# Make sure we have a dbus object representing it
		pv_dests = []
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			if len(pv_dests_and_ranges):
				for pr in pv_dests_and_ranges:
					pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
					if not pv_dbus_obj:
						raise dbus.exceptions.DBusException(
							VG_INTERFACE,
							'PV Destination (%s) not found' % pr[0])

					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))

			rc, out, err = cmdhandler.vg_lv_create(
				vg_name, create_options, name, size_bytes, pv_dests)

			if rc == 0:
				return Vg.fetch_new_lv(vg_name, name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='sta(ott)ia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def LvCreate(self, name, size_bytes, pv_dests_and_ranges,
			tmo, create_options, cb, cbe):
		"""
		This one it for the advanced users that want to roll their own
		:param name:            Name of the LV
		:param size_bytes:      Size of LV in bytes
		:param pv_dests_and_ranges:   Optional array of PV object paths and
									ranges
		:param tmo: -1 == Wait forever, 0 == return job immediately, > 0 ==
							willing to wait that number of seconds before
							getting a job
		:param create_options:  hash of key/value pairs
		:param cb: Internal, not accessible by dbus API user
		:param cbe: Internal, not accessible by dbus API user
		:return: (oo) First object path is newly created object, second is
					job object path if created.  Each == '/' when it doesn't
					apply.
		"""
		utils.validate_lv_name(VG_INTERFACE, self.Name, name)
		r = RequestEntry(tmo, Vg._lv_create,
				(self.state.Uuid, self.state.lvm_id,
				name, round_size(size_bytes), pv_dests_and_ranges,
				create_options), cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _lv_create_linear(uuid, vg_name, name, size_bytes,
			thin_pool, create_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_lv_create_linear(
				vg_name, create_options, name, size_bytes, thin_pool)

			if rc == 0:
				created_lv = Vg.fetch_new_lv(vg_name, name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

		return created_lv

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='stbia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def LvCreateLinear(self, name, size_bytes,
			thin_pool, tmo, create_options, cb, cbe):
		utils.validate_lv_name(VG_INTERFACE, self.Name, name)
		r = RequestEntry(tmo, Vg._lv_create_linear,
						(self.state.Uuid, self.state.lvm_id,
						name, round_size(size_bytes), thin_pool,
						create_options), cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes,
			stripe_size_kb, thin_pool, create_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_lv_create_striped(
				vg_name, create_options, name, size_bytes,
				num_stripes, stripe_size_kb, thin_pool)
			if rc == 0:
				created_lv = Vg.fetch_new_lv(vg_name, name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE, 'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

		return created_lv

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='stuubia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def LvCreateStriped(self, name, size_bytes, num_stripes,
						stripe_size_kb, thin_pool, tmo, create_options,
						cb, cbe):
		utils.validate_lv_name(VG_INTERFACE, self.Name, name)
		r = RequestEntry(
				tmo, Vg._lv_create_striped,
				(self.state.Uuid, self.state.lvm_id, name,
				round_size(size_bytes), num_stripes, stripe_size_kb,
				thin_pool, create_options),
				cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _lv_create_mirror(uuid, vg_name, name, size_bytes,
			num_copies, create_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_lv_create_mirror(
				vg_name, create_options, name, size_bytes, num_copies)
			if rc == 0:
				created_lv = Vg.fetch_new_lv(vg_name, name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

		return created_lv

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='stuia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def LvCreateMirror(self, name, size_bytes, num_copies,
			tmo, create_options, cb, cbe):
		utils.validate_lv_name(VG_INTERFACE, self.Name, name)
		r = RequestEntry(
			tmo, Vg._lv_create_mirror,
			(self.state.Uuid, self.state.lvm_id, name,
			round_size(size_bytes), num_copies,
			create_options), cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes,
						num_stripes, stripe_size_kb, create_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.vg_lv_create_raid(
				vg_name, create_options, name, raid_type, size_bytes,
				num_stripes, stripe_size_kb)
			if rc == 0:
				created_lv = Vg.fetch_new_lv(vg_name, name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

		return created_lv

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='sstuuia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def LvCreateRaid(self, name, raid_type, size_bytes,
			num_stripes, stripe_size_kb, tmo,
			create_options, cb, cbe):
		utils.validate_lv_name(VG_INTERFACE, self.Name, name)
		r = RequestEntry(tmo, Vg._lv_create_raid,
				(self.state.Uuid, self.state.lvm_id, name,
				raid_type, round_size(size_bytes), num_stripes,
				stripe_size_kb, create_options), cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _create_pool(uuid, vg_name, meta_data_lv, data_lv,
						create_options, create_method):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		# Retrieve the full names for the metadata and data lv
		md = cfg.om.get_object_by_path(meta_data_lv)
		data = cfg.om.get_object_by_path(data_lv)

		if dbo and md and data:

			new_name = data.Name

			rc, out, err = create_method(
				md.lv_full_name(), data.lv_full_name(), create_options)
			if rc == 0:
				cfg.om.remove_object(md, emit_signal=True)
				cfg.om.remove_object(data, emit_signal=True)

				cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			msg = ""

			if not dbo:
				msg += 'VG with uuid %s and name %s not present!' % \
					(uuid, vg_name)

			if not md:
				msg += 'Meta data LV with object path %s not present!' % \
					(meta_data_lv)

			if not data_lv:
				msg += 'Data LV with object path %s not present!' % \
					(meta_data_lv)

			raise dbus.exceptions.DBusException(VG_INTERFACE, msg)

		return cache_pool_lv

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='ooia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def CreateCachePool(self, meta_data_lv, data_lv, tmo, create_options,
						cb, cbe):
		r = RequestEntry(
			tmo, Vg._create_pool,
			(self.state.Uuid, self.state.lvm_id, meta_data_lv,
			data_lv, create_options, cmdhandler.vg_create_cache_pool), cb, cbe)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='ooia{sv}',
		out_signature='(oo)',
		async_callbacks=('cb', 'cbe'))
	def CreateThinPool(self, meta_data_lv, data_lv, tmo, create_options,
						cb, cbe):
		r = RequestEntry(
			tmo, Vg._create_pool,
			(self.state.Uuid, self.state.lvm_id, meta_data_lv,
			data_lv, create_options, cmdhandler.vg_create_thin_pool), cb, cbe)
		cfg.worker_q.put(r)

	@staticmethod
	def _pv_add_rm_tags(uuid, vg_name, pv_object_paths, tags_add,
						tags_del, tag_options):
		pv_devices = []

		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			# Check for existence of pv object paths
			for p in pv_object_paths:
				pv = cfg.om.get_object_by_path(p)
				if pv:
					pv_devices.append(pv.Name)
				else:
					raise dbus.exceptions.DBusException(
						VG_INTERFACE, 'PV object path = %s not found' % p)

			rc, out, err = cmdhandler.pv_tag(
				pv_devices, tags_add, tags_del, tag_options)
			if rc == 0:
				cfg.load()
				return '/'
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='aoasia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def PvTagsAdd(self, pvs, tags, tmo, tag_options, cb, cbe):

		for t in tags:
			utils.validate_tag(VG_INTERFACE, t)

		r = RequestEntry(tmo, Vg._pv_add_rm_tags,
				(self.state.Uuid, self.state.lvm_id,
				pvs, tags, None, tag_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='aoasia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def PvTagsDel(self, pvs, tags, tmo, tag_options, cb, cbe):

		for t in tags:
			utils.validate_tag(VG_INTERFACE, t)

		r = RequestEntry(
			tmo, Vg._pv_add_rm_tags,
			(self.state.Uuid, self.state.lvm_id,
			pvs, None, tags, tag_options),
			cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@staticmethod
	def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:

			rc, out, err = cmdhandler.vg_tag(
				vg_name, tags_add, tags_del, tag_options)
			if rc == 0:
				dbo.refresh()
				return '/'
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='asia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def TagsAdd(self, tags, tmo, tag_options, cb, cbe):

		for t in tags:
			utils.validate_tag(VG_INTERFACE, t)

		r = RequestEntry(tmo, Vg._vg_add_rm_tags,
				(self.state.Uuid, self.state.lvm_id,
				tags, None, tag_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='asia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def TagsDel(self, tags, tmo, tag_options, cb, cbe):

		for t in tags:
			utils.validate_tag(VG_INTERFACE, t)

		r = RequestEntry(tmo, Vg._vg_add_rm_tags,
				(self.state.Uuid, self.state.lvm_id,
				None, tags, tag_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@staticmethod
	def _vg_change_set(uuid, vg_name, method, value, options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = method(vg_name, value, options)
			if rc == 0:
				dbo.refresh()
				return '/'
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))

		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='sia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def AllocationPolicySet(self, policy, tmo, policy_options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_change_set,
				(self.state.Uuid, self.state.lvm_id,
				cmdhandler.vg_allocation_policy,
				policy, policy_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='tia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def MaxPvSet(self, number, tmo, max_options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_change_set,
				(self.state.Uuid, self.state.lvm_id,
				cmdhandler.vg_max_pv, number, max_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='ia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def UuidGenerate(self, tmo, options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_change_set,
				(self.state.Uuid, self.state.lvm_id,
				cmdhandler.vg_uuid_gen, None, options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	def _attribute(self, pos, ch):
		if self.state.attr[pos] == ch:
			return True
		return False

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='tia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def MaxLvSet(self, number, tmo, max_options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_change_set,
				(self.state.Uuid, self.state.lvm_id,
				cmdhandler.vg_max_lv, number, max_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@staticmethod
	def _vg_activate_deactivate(uuid, vg_name, activate, control_flags,
								options):
		# Make sure we have a dbus object representing it
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)

		if dbo:
			rc, out, err = cmdhandler.activate_deactivate(
				'vgchange', vg_name, activate, control_flags, options)
			if rc == 0:
				cfg.load()
				return '/'
			else:
				raise dbus.exceptions.DBusException(
					VG_INTERFACE,
					'Exit code %s, stderr = %s' % (str(rc), err))
		else:
			raise dbus.exceptions.DBusException(
				VG_INTERFACE,
				'VG with uuid %s and name %s not present!' %
				(uuid, vg_name))

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='tia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Activate(self, control_flags, tmo, activate_options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_activate_deactivate,
				(self.state.Uuid, self.state.lvm_id, True,
				control_flags, activate_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@dbus.service.method(
		dbus_interface=VG_INTERFACE,
		in_signature='tia{sv}',
		out_signature='o',
		async_callbacks=('cb', 'cbe'))
	def Deactivate(self, control_flags, tmo, activate_options, cb, cbe):
		r = RequestEntry(tmo, Vg._vg_activate_deactivate,
				(self.state.Uuid, self.state.lvm_id, False,
				control_flags, activate_options),
				cb, cbe, return_tuple=False)
		cfg.worker_q.put(r)

	@property
	def Tags(self):
		return utils.parse_tags(self.state.tags)

	@property
	def Pvs(self):
		return self.state.Pvs

	@property
	def Lvs(self):
		return self.state.Lvs

	@property
	def lvm_id(self):
		return self.state.lvm_id

	@property
	def Writeable(self):
		return self._attribute(0, 'w')

	@property
	def Readable(self):
		return self._attribute(0, 'r')

	@property
	def Resizeable(self):
		return self._attribute(1, 'z')

	@property
	def Exportable(self):
		return self._attribute(2, 'x')

	@property
	def Partial(self):
		return self._attribute(3, 'p')

	@property
	def AllocContiguous(self):
		return self._attribute(4, 'c')

	@property
	def AllocCling(self):
		return self._attribute(4, 'l')

	@property
	def AllocNormal(self):
		return self._attribute(4, 'n')

	@property
	def AllocAnywhere(self):
		return self._attribute(4, 'a')

	@property
	def Clustered(self):
		return self._attribute(5, 'c')
