from bootstrapvz.base import Task
from bootstrapvz.common import phases
from bootstrapvz.common.tasks import workspace
import os
import shutil

assets = os.path.normpath(os.path.join(os.path.dirname(__file__), 'assets'))


class CheckOVAPath(Task):
	description = 'Checking if the OVA file already exists'
	phase = phases.preparation

	@classmethod
	def run(cls, info):
		ova_basename = info.manifest.name.format(**info.manifest_vars)
		ova_name = ova_basename + '.ova'
		ova_path = os.path.join(info.manifest.bootstrapper['workspace'], ova_name)
		if os.path.exists(ova_path):
			from bootstrapvz.common.exceptions import TaskError
			msg = 'The OVA `{name}\' already exists at `{path}\''.format(name=ova_name, path=ova_path)
			raise TaskError(msg)
		info._ova['ova_basename'] = ova_basename
		info._ova['ova_name'] = ova_name
		info._ova['ova_path'] = ova_path


class CreateOVADir(Task):
	description = 'Creating directory for the OVA'
	phase = phases.preparation
	predecessors = [workspace.CreateWorkspace, CheckOVAPath]

	@classmethod
	def run(cls, info):
		info._ova['folder'] = os.path.join(info.workspace, 'ova')
		os.mkdir(info._ova['folder'])


class PackageOVA(Task):
	description = 'Packaging the volume as an OVA'
	phase = phases.image_registration

	@classmethod
	def run(cls, info):
		import random
		mac_address = '080027{mac:06X}'.format(mac=random.randrange(16 ** 6))
		
		from bootstrapvz.common.tools import log_check_call
		disk_name = info._ova['ova_basename'] + '.' + info.volume.extension
		disk_link = os.path.join(info._ova['folder'], disk_name)
		log_check_call(['ln', '-s', info.volume.image_path, disk_link])

		ovf_path = os.path.join(info._ova['folder'], info._ova['ova_basename'] + '.ovf')
		cls.write_ovf(info, ovf_path, mac_address, disk_name)

		ova_files = os.listdir(info._ova['folder'])
		log_check_call(['ovftool', ovf_path, info._ova['ova_path']]
		               )
		import logging
		logging.getLogger(__name__).info('The OVA has been placed at ' + info._ova['ova_path'])

	@classmethod
	def write_ovf(cls, info, destination, mac_address, disk_name):
		namespaces = {'ovf':     'http://schemas.dmtf.org/ovf/envelope/1',
		              'rasd':    'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData',
		              'vssd':    'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData',
		              'xsi':     'http://www.w3.org/2001/XMLSchema-instance',
		              'vbox':    'http://www.virtualbox.org/ovf/machine',
		              }

		def attr(element, name, value=None):
			for prefix, ns in namespaces.iteritems():
				name = name.replace(prefix + ':', '{' + ns + '}')
			if value is None:
				return element.attrib[name]
			else:
				element.attrib[name] = str(value)

		template_path = os.path.join(assets, 'default.ovf')
		if 'ovf' in info.manifest.plugins['ova']:
			template_path = info.manifest.plugins['ova']['ovf']
		
		import xml.etree.ElementTree as ET
		template = ET.parse(template_path)
		root = template.getroot()

		[disk_ref] = root.findall('./ovf:References/ovf:File', namespaces)
		attr(disk_ref, 'ovf:href', disk_name)

		# List of OVF disk format URIs
		# Snatched from VBox source (src/VBox/Main/src-server/ApplianceImpl.cpp:47)
		# ISOURI = "http://www.ecma-international.org/publications/standards/Ecma-119.htm"
		# VMDKStreamURI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
		# VMDKSparseURI = "http://www.vmware.com/specifications/vmdk.html#sparse"
		# VMDKCompressedURI = "http://www.vmware.com/specifications/vmdk.html#compressed"
		# VMDKCompressedURI2 = "http://www.vmware.com/interfaces/specifications/vmdk.html#compressed"
		# VHDURI = "http://go.microsoft.com/fwlink/?LinkId=137171"
		volume_uuid = info.volume.get_uuid()
		[disk] = root.findall('./ovf:DiskSection/ovf:Disk', namespaces)
		attr(disk, 'ovf:capacity', info.volume.size.bytes.get_qty_in('B'))
		attr(disk, 'ovf:format', info.volume.ovf_uri)
		attr(disk, 'vbox:uuid', volume_uuid)

		[system] = root.findall('./ovf:VirtualSystem', namespaces)
		attr(system, 'ovf:id', info._ova['ova_basename'])

		# Set the operating system
		[os_section] = system.findall('./ovf:OperatingSystemSection', namespaces)
		os_info = {'i386': {'id': 96, 'name': 'Debian'},
		           'amd64': {'id': 96, 'name': 'Debian_64'}
		           }.get(info.manifest.system['architecture'])
		attr(os_section, 'ovf:id', os_info['id'])
		[os_desc] = os_section.findall('./ovf:Description', namespaces)
		os_desc.text = os_info['name']
		[os_type] = os_section.findall('./vbox:OSType', namespaces)
		os_type.text = os_info['name']

		[sysid] = system.findall('./ovf:VirtualHardwareSection/ovf:System/'
		                         'vssd:VirtualSystemIdentifier', namespaces)
		sysid.text = info._ova['ova_basename']

		[machine] = system.findall('./vbox:Machine', namespaces)
		import uuid
		del machine.attrib['uuid']
		attr(machine, 'uuid', uuid.uuid4())
		del machine.attrib['name']
		attr(machine, 'name', info._ova['ova_basename'])
		from datetime import datetime
		del machine.attrib['lastStateChange']
		attr(machine, 'lastStateChange', datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
		[nic] = machine.findall('./ovf:Hardware/ovf:Network/ovf:Adapter', namespaces)
		attr(machine, 'MACAddress', mac_address)

		[device_img] = machine.findall('./ovf:StorageControllers'
		                               '/ovf:StorageController[1]'
		                               '/ovf:AttachedDevice/ovf:Image', namespaces)
		attr(device_img, 'uuid', '{' + str(volume_uuid) + '}')

		template.write(destination, xml_declaration=True)  # , default_namespace=namespaces['ovf']


class RemoveOVADir(Task):
	description = 'Removing the OVA directory'
	phase = phases.cleaning
	successors = [workspace.DeleteWorkspace]

	@classmethod
	def run(cls, info):
		shutil.rmtree(info._ova['folder'])
		del info._ova['folder']

[((7, 39, 7, 64), 'os.path.dirname', 'os.path.dirname', ({(7, 55, 7, 63): '__file__'}, {}), '(__file__)', False, 'import os\n'), ((18, 13, 18, 76), 'os.path.join', 'os.path.join', ({(18, 26, 18, 65): "info.manifest.bootstrapper['workspace']", (18, 67, 18, 75): 'ova_name'}, {}), "(info.manifest.bootstrapper['workspace'], ova_name)", False, 'import os\n'), ((19, 5, 19, 29), 'os.path.exists', 'os.path.exists', ({(19, 20, 19, 28): 'ova_path'}, {}), '(ova_path)', False, 'import os\n'), ((35, 24, 35, 59), 'os.path.join', 'os.path.join', ({(35, 37, 35, 51): 'info.workspace', (35, 53, 35, 58): '"""ova"""'}, {}), "(info.workspace, 'ova')", False, 'import os\n'), ((36, 2, 36, 31), 'os.mkdir', 'os.mkdir', ({(36, 11, 36, 30): "info._ova['folder']"}, {}), "(info._ova['folder'])", False, 'import os\n'), ((50, 14, 50, 58), 'os.path.join', 'os.path.join', ({(50, 27, 50, 46): "info._ova['folder']", (50, 48, 50, 57): 'disk_name'}, {}), "(info._ova['folder'], disk_name)", False, 'import os\n'), ((51, 2, 51, 65), 'bootstrapvz.common.tools.log_check_call', 'log_check_call', ({(51, 17, 51, 64): "['ln', '-s', info.volume.image_path, disk_link]"}, {}), "(['ln', '-s', info.volume.image_path, disk_link])", False, 'from bootstrapvz.common.tools import log_check_call\n'), ((53, 13, 53, 82), 'os.path.join', 'os.path.join', ({(53, 26, 53, 45): "info._ova['folder']", (53, 47, 53, 81): "info._ova['ova_basename'] + '.ovf'"}, {}), "(info._ova['folder'], info._ova['ova_basename'] + '.ovf')", False, 'import os\n'), ((56, 14, 56, 45), 'os.listdir', 'os.listdir', ({(56, 25, 56, 44): "info._ova['folder']"}, {}), "(info._ova['folder'])", False, 'import os\n'), ((57, 2, 58, 18), 'bootstrapvz.common.tools.log_check_call', 'log_check_call', ({(57, 17, 57, 61): "['ovftool', ovf_path, info._ova['ova_path']]"}, {}), "(['ovftool', ovf_path, info._ova['ova_path']])", False, 'from bootstrapvz.common.tools import log_check_call\n'), ((79, 18, 79, 53), 'os.path.join', 'os.path.join', ({(79, 31, 79, 37): 'assets', (79, 39, 79, 52): '"""default.ovf"""'}, {}), "(assets, 'default.ovf')", False, 'import os\n'), ((84, 13, 84, 36), 'xml.etree.ElementTree.parse', 'ET.parse', ({(84, 22, 84, 35): 'template_path'}, {}), '(template_path)', True, 'import xml.etree.ElementTree as ET\n'), ((149, 2, 149, 36), 'shutil.rmtree', 'shutil.rmtree', ({(149, 16, 149, 35): "info._ova['folder']"}, {}), "(info._ova['folder'])", False, 'import shutil\n'), ((22, 9, 22, 23), 'bootstrapvz.common.exceptions.TaskError', 'TaskError', ({(22, 19, 22, 22): 'msg'}, {}), '(msg)', False, 'from bootstrapvz.common.exceptions import TaskError\n'), ((125, 24, 125, 36), 'uuid.uuid4', 'uuid.uuid4', ({}, {}), '()', False, 'import uuid\n'), ((46, 45, 46, 70), 'random.randrange', 'random.randrange', ({(46, 62, 46, 69): '16 ** 6'}, {}), '(16 ** 6)', False, 'import random\n'), ((60, 2, 60, 29), 'logging.getLogger', 'logging.getLogger', ({(60, 20, 60, 28): '__name__'}, {}), '(__name__)', False, 'import logging\n'), ((130, 35, 130, 49), 'datetime.datetime.now', 'datetime.now', ({}, {}), '()', False, 'from datetime import datetime\n')]