#!/usr/bin/python

#
#   Copyright 2017 RIFT.IO Inc
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

import re
import gi

gi.require_version('RwcalYang', '1.0')
from gi.repository import RwcalYang


class GuestEPAUtils(object):
    """
    Utility class for Host EPA to Openstack flavor extra_specs conversion routines
    """
    def __init__(self):
        self._mano_to_espec_cpu_pinning_policy = {
            'DEDICATED' : 'dedicated',
            'SHARED'    : 'shared',
            'ANY'       : 'any',
        }

        self._espec_to_mano_cpu_pinning_policy = {
            'dedicated' : 'DEDICATED',
            'shared'    : 'SHARED',
            'any'       : 'ANY',
        }
        
        self._mano_to_espec_mempage_size = {
            'LARGE'        : 'large', 
            'SMALL'        : 'small',
            'SIZE_2MB'     :  2048,
            'SIZE_1GB'     :  1048576,
            'PREFER_LARGE' : 'large',
        }

        self._espec_to_mano_mempage_size = {
            'large'        : 'LARGE', 
            'small'        : 'SMALL',
             2048          : 'SIZE_2MB',
             1048576       : 'SIZE_1GB',
            'large'        : 'PREFER_LARGE',
        }

        self._mano_to_espec_cpu_thread_pinning_policy = {
            'AVOID'    : 'avoid',
            'SEPARATE' : 'separate',
            'ISOLATE'  : 'isolate',
            'PREFER'   : 'prefer',
        }

        self._espec_to_mano_cpu_thread_pinning_policy = {
            'avoid'    : 'AVOID',
            'separate' : 'SEPARATE',
            'isolate'  : 'ISOLATE',
            'prefer'   : 'PREFER',
        }

        self._espec_to_mano_numa_memory_policy = {
            'strict'   : 'STRICT',
            'preferred': 'PREFERRED'
        }

        self._mano_to_espec_numa_memory_policy = {
            'STRICT'   : 'strict',
            'PREFERRED': 'preferred'
        }

    def mano_to_extra_spec_cpu_pinning_policy(self, cpu_pinning_policy):
        if cpu_pinning_policy in self._mano_to_espec_cpu_pinning_policy:
            return self._mano_to_espec_cpu_pinning_policy[cpu_pinning_policy]
        else:
            return None

    def extra_spec_to_mano_cpu_pinning_policy(self, cpu_pinning_policy):
        if cpu_pinning_policy in self._espec_to_mano_cpu_pinning_policy:
            return self._espec_to_mano_cpu_pinning_policy[cpu_pinning_policy]
        else:
            return None

    def mano_to_extra_spec_mempage_size(self, mempage_size):
        if mempage_size in self._mano_to_espec_mempage_size:
            return self._mano_to_espec_mempage_size[mempage_size]
        else:
            return None
        
    def extra_spec_to_mano_mempage_size(self, mempage_size):
        if mempage_size in self._espec_to_mano_mempage_size:
            return self._espec_to_mano_mempage_size[mempage_size]
        else:
            return None

    def mano_to_extra_spec_cpu_thread_pinning_policy(self, cpu_thread_pinning_policy):
        if cpu_thread_pinning_policy in self._mano_to_espec_cpu_thread_pinning_policy:
            return self._mano_to_espec_cpu_thread_pinning_policy[cpu_thread_pinning_policy]
        else:
            return None

    def extra_spec_to_mano_cpu_thread_pinning_policy(self, cpu_thread_pinning_policy):
        if cpu_thread_pinning_policy in self._espec_to_mano_cpu_thread_pinning_policy:
            return self._espec_to_mano_cpu_thread_pinning_policy[cpu_thread_pinning_policy]
        else:
            return None

    def mano_to_extra_spec_trusted_execution(self, trusted_execution):
        if trusted_execution:
            return 'trusted'
        else:
            return 'untrusted'

    def extra_spec_to_mano_trusted_execution(self, trusted_execution):
        if trusted_execution == 'trusted':
            return True
        elif trusted_execution == 'untrusted':
            return False
        else:
            return None
        
    def mano_to_extra_spec_numa_node_count(self, numa_node_count):
        return numa_node_count

    def extra_specs_to_mano_numa_node_count(self, numa_node_count):
        return int(numa_node_count)
    
    def mano_to_extra_spec_numa_memory_policy(self, numa_memory_policy):
        if numa_memory_policy in self._mano_to_espec_numa_memory_policy:
            return self._mano_to_espec_numa_memory_policy[numa_memory_policy]
        else:
            return None

    def extra_to_mano_spec_numa_memory_policy(self, numa_memory_policy):
        if numa_memory_policy in self._espec_to_mano_numa_memory_policy:
            return self._espec_to_mano_numa_memory_policy[numa_memory_policy]
        else:
            return None
        
                                                          
    
    
class HostEPAUtils(object):
    """
    Utility class for Host EPA to Openstack flavor extra_specs conversion routines
    """
    def __init__(self):
        self._mano_to_espec_cpumodel = {
            "PREFER_WESTMERE"     : "Westmere",
            "REQUIRE_WESTMERE"    : "Westmere",
            "PREFER_SANDYBRIDGE"  : "SandyBridge",
            "REQUIRE_SANDYBRIDGE" : "SandyBridge",
            "PREFER_IVYBRIDGE"    : "IvyBridge",
            "REQUIRE_IVYBRIDGE"   : "IvyBridge",
            "PREFER_HASWELL"      : "Haswell",
            "REQUIRE_HASWELL"     : "Haswell",
            "PREFER_BROADWELL"    : "Broadwell",
            "REQUIRE_BROADWELL"   : "Broadwell",
            "PREFER_NEHALEM"      : "Nehalem",
            "REQUIRE_NEHALEM"     : "Nehalem",
            "PREFER_PENRYN"       : "Penryn",
            "REQUIRE_PENRYN"      : "Penryn",
            "PREFER_CONROE"       : "Conroe",
            "REQUIRE_CONROE"      : "Conroe",
            "PREFER_CORE2DUO"     : "Core2Duo",
            "REQUIRE_CORE2DUO"    : "Core2Duo",
        }

        self._espec_to_mano_cpumodel = {
            "Westmere"     : "REQUIRE_WESTMERE",
            "SandyBridge"  : "REQUIRE_SANDYBRIDGE",
            "IvyBridge"    : "REQUIRE_IVYBRIDGE",
            "Haswell"      : "REQUIRE_HASWELL",
            "Broadwell"    : "REQUIRE_BROADWELL",
            "Nehalem"      : "REQUIRE_NEHALEM",
            "Penryn"       : "REQUIRE_PENRYN",
            "Conroe"       : "REQUIRE_CONROE",
            "Core2Duo"     : "REQUIRE_CORE2DUO",
        }

        self._mano_to_espec_cpuarch = {
            "PREFER_X86"     : "x86",
            "REQUIRE_X86"    : "x86",
            "PREFER_X86_64"  : "x86_64",
            "REQUIRE_X86_64" : "x86_64",
            "PREFER_I686"    : "i686",
            "REQUIRE_I686"   : "i686",
            "PREFER_IA64"    : "ia64",
            "REQUIRE_IA64"   : "ia64",
            "PREFER_ARMV7"   : "ARMv7",
            "REQUIRE_ARMV7"  : "ARMv7",
            "PREFER_ARMV8"   : "ARMv8-A",
            "REQUIRE_ARMV8"  : "ARMv8-A",
        }

        self._espec_to_mano_cpuarch = {
            "x86"     : "REQUIRE_X86",
            "x86_64"  : "REQUIRE_X86_64",
            "i686"    : "REQUIRE_I686",
            "ia64"    : "REQUIRE_IA64",
            "ARMv7-A" : "REQUIRE_ARMV7",
            "ARMv8-A" : "REQUIRE_ARMV8",
        }

        self._mano_to_espec_cpuvendor = {
            "PREFER_INTEL"  : "Intel",
            "REQUIRE_INTEL" : "Intel",
            "PREFER_AMD"    : "AMD",
            "REQUIRE_AMD"   : "AMD",
        }

        self._espec_to_mano_cpuvendor = {
            "Intel" : "REQUIRE_INTEL",
            "AMD"   : "REQUIRE_AMD",
        }

        self._mano_to_espec_cpufeatures = {
            "PREFER_AES"       : "aes",
            "REQUIRE_AES"      : "aes",
            "REQUIRE_VME"      : "vme",
            "PREFER_VME"       : "vme",
            "REQUIRE_DE"       : "de",
            "PREFER_DE"        : "de",
            "REQUIRE_PSE"      : "pse",
            "PREFER_PSE"       : "pse",
            "REQUIRE_TSC"      : "tsc",
            "PREFER_TSC"       : "tsc",
            "REQUIRE_MSR"      : "msr",
            "PREFER_MSR"       : "msr",
            "REQUIRE_PAE"      : "pae",
            "PREFER_PAE"       : "pae",
            "REQUIRE_MCE"      : "mce",
            "PREFER_MCE"       : "mce",
            "REQUIRE_CX8"      : "cx8",
            "PREFER_CX8"       : "cx8",
            "REQUIRE_APIC"     : "apic",
            "PREFER_APIC"      : "apic",
            "REQUIRE_SEP"      : "sep",
            "PREFER_SEP"       : "sep",
            "REQUIRE_MTRR"     : "mtrr",
            "PREFER_MTRR"      : "mtrr",
            "REQUIRE_PGE"      : "pge",
            "PREFER_PGE"       : "pge",
            "REQUIRE_MCA"      : "mca",
            "PREFER_MCA"       : "mca",
            "REQUIRE_CMOV"     : "cmov",
            "PREFER_CMOV"      : "cmov",
            "REQUIRE_PAT"      : "pat",
            "PREFER_PAT"       : "pat",
            "REQUIRE_PSE36"    : "pse36",
            "PREFER_PSE36"     : "pse36",
            "REQUIRE_CLFLUSH"  : "clflush",
            "PREFER_CLFLUSH"   : "clflush",
            "REQUIRE_DTS"      : "dts",
            "PREFER_DTS"       : "dts",
            "REQUIRE_ACPI"     : "acpi",
            "PREFER_ACPI"      : "acpi",
            "REQUIRE_MMX"      : "mmx",
            "PREFER_MMX"       : "mmx",
            "REQUIRE_FXSR"     : "fxsr",
            "PREFER_FXSR"      : "fxsr",
            "REQUIRE_SSE"      : "sse",
            "PREFER_SSE"       : "sse",
            "REQUIRE_SSE2"     : "sse2",
            "PREFER_SSE2"      : "sse2",
            "REQUIRE_SS"       : "ss",
            "PREFER_SS"        : "ss",
            "REQUIRE_HT"       : "ht",
            "PREFER_HT"        : "ht",
            "REQUIRE_TM"       : "tm",
            "PREFER_TM"        : "tm",
            "REQUIRE_IA64"     : "ia64",
            "PREFER_IA64"      : "ia64",
            "REQUIRE_PBE"      : "pbe",
            "PREFER_PBE"       : "pbe",
            "REQUIRE_RDTSCP"   : "rdtscp",
            "PREFER_RDTSCP"    : "rdtscp",
            "REQUIRE_PNI"      : "pni",
            "PREFER_PNI"       : "pni",
            "REQUIRE_PCLMULQDQ": "pclmulqdq",
            "PREFER_PCLMULQDQ" : "pclmulqdq",
            "REQUIRE_DTES64"   : "dtes64",
            "PREFER_DTES64"    : "dtes64",
            "REQUIRE_MONITOR"  : "monitor",
            "PREFER_MONITOR"   : "monitor",
            "REQUIRE_DS_CPL"   : "ds_cpl",
            "PREFER_DS_CPL"    : "ds_cpl",
            "REQUIRE_VMX"      : "vmx",
            "PREFER_VMX"       : "vmx",
            "REQUIRE_SMX"      : "smx",
            "PREFER_SMX"       : "smx",
            "REQUIRE_EST"      : "est",
            "PREFER_EST"       : "est",
            "REQUIRE_TM2"      : "tm2",
            "PREFER_TM2"       : "tm2",
            "REQUIRE_SSSE3"    : "ssse3",
            "PREFER_SSSE3"     : "ssse3",
            "REQUIRE_CID"      : "cid",
            "PREFER_CID"       : "cid",
            "REQUIRE_FMA"      : "fma",
            "PREFER_FMA"       : "fma",
            "REQUIRE_CX16"     : "cx16",
            "PREFER_CX16"      : "cx16",
            "REQUIRE_XTPR"     : "xtpr",
            "PREFER_XTPR"      : "xtpr",
            "REQUIRE_PDCM"     : "pdcm",
            "PREFER_PDCM"      : "pdcm",
            "REQUIRE_PCID"     : "pcid",
            "PREFER_PCID"      : "pcid",
            "REQUIRE_DCA"      : "dca",
            "PREFER_DCA"       : "dca",
            "REQUIRE_SSE4_1"   : "sse4_1",
            "PREFER_SSE4_1"    : "sse4_1",
            "REQUIRE_SSE4_2"   : "sse4_2",
            "PREFER_SSE4_2"    : "sse4_2",
            "REQUIRE_X2APIC"   : "x2apic",
            "PREFER_X2APIC"    : "x2apic",
            "REQUIRE_MOVBE"    : "movbe",
            "PREFER_MOVBE"     : "movbe",
            "REQUIRE_POPCNT"   : "popcnt",
            "PREFER_POPCNT"    : "popcnt",
            "REQUIRE_TSC_DEADLINE_TIMER"   : "tsc_deadline_timer",
            "PREFER_TSC_DEADLINE_TIMER"    : "tsc_deadline_timer",
            "REQUIRE_XSAVE"    : "xsave",
            "PREFER_XSAVE"     : "xsave",
            "REQUIRE_AVX"      : "avx",
            "PREFER_AVX"       : "avx",
            "REQUIRE_F16C"     : "f16c",
            "PREFER_F16C"      : "f16c",
            "REQUIRE_RDRAND"   : "rdrand",
            "PREFER_RDRAND"    : "rdrand",
            "REQUIRE_FSGSBASE" : "fsgsbase",
            "PREFER_FSGSBASE"  : "fsgsbase",
            "REQUIRE_BMI1"     : "bmi1",
            "PREFER_BMI1"      : "bmi1",
            "REQUIRE_HLE"      : "hle",
            "PREFER_HLE"       : "hle",
            "REQUIRE_AVX2"     : "avx2",
            "PREFER_AVX2"      : "avx2",
            "REQUIRE_SMEP"     : "smep",
            "PREFER_SMEP"      : "smep",
            "REQUIRE_BMI2"     : "bmi2",
            "PREFER_BMI2"      : "bmi2",
            "REQUIRE_ERMS"     : "erms",
            "PREFER_ERMS"      : "erms",
            "REQUIRE_INVPCID"  : "invpcid",
            "PREFER_INVPCID"   : "invpcid",
            "REQUIRE_RTM"      : "rtm",
            "PREFER_RTM"       : "rtm",
            "REQUIRE_MPX"      : "mpx",
            "PREFER_MPX"       : "mpx",
            "REQUIRE_RDSEED"   : "rdseed",
            "PREFER_RDSEED"    : "rdseed",
            "REQUIRE_ADX"      : "adx",
            "PREFER_ADX"       : "adx",
            "REQUIRE_SMAP"     : "smap",
            "PREFER_SMAP"      : "smap",
        }

        self._espec_to_mano_cpufeatures = {
            "aes"      : "REQUIRE_AES",
            "vme"      : "REQUIRE_VME",
            "de"       : "REQUIRE_DE",
            "pse"      : "REQUIRE_PSE",
            "tsc"      : "REQUIRE_TSC",
            "msr"      : "REQUIRE_MSR",
            "pae"      : "REQUIRE_PAE",
            "mce"      : "REQUIRE_MCE",
            "cx8"      : "REQUIRE_CX8",
            "apic"     : "REQUIRE_APIC",
            "sep"      : "REQUIRE_SEP",
            "mtrr"     : "REQUIRE_MTRR",
            "pge"      : "REQUIRE_PGE",
            "mca"      : "REQUIRE_MCA",
            "cmov"     : "REQUIRE_CMOV",
            "pat"      : "REQUIRE_PAT",
            "pse36"    : "REQUIRE_PSE36",
            "clflush"  : "REQUIRE_CLFLUSH",
            "dts"      : "REQUIRE_DTS",
            "acpi"     : "REQUIRE_ACPI",
            "mmx"      : "REQUIRE_MMX",
            "fxsr"     : "REQUIRE_FXSR",
            "sse"      : "REQUIRE_SSE",
            "sse2"     : "REQUIRE_SSE2",
            "ss"       : "REQUIRE_SS",
            "ht"       : "REQUIRE_HT",
            "tm"       : "REQUIRE_TM",
            "ia64"     : "REQUIRE_IA64",
            "pbe"      : "REQUIRE_PBE",
            "rdtscp"   : "REQUIRE_RDTSCP",
            "pni"      : "REQUIRE_PNI",
            "pclmulqdq": "REQUIRE_PCLMULQDQ",
            "dtes64"   : "REQUIRE_DTES64",
            "monitor"  : "REQUIRE_MONITOR",
            "ds_cpl"   : "REQUIRE_DS_CPL",
            "vmx"      : "REQUIRE_VMX",
            "smx"      : "REQUIRE_SMX",
            "est"      : "REQUIRE_EST",
            "tm2"      : "REQUIRE_TM2",
            "ssse3"    : "REQUIRE_SSSE3",
            "cid"      : "REQUIRE_CID",
            "fma"      : "REQUIRE_FMA",
            "cx16"     : "REQUIRE_CX16",
            "xtpr"     : "REQUIRE_XTPR",
            "pdcm"     : "REQUIRE_PDCM",
            "pcid"     : "REQUIRE_PCID",
            "dca"      : "REQUIRE_DCA",
            "sse4_1"   : "REQUIRE_SSE4_1",
            "sse4_2"   : "REQUIRE_SSE4_2",
            "x2apic"   : "REQUIRE_X2APIC",
            "movbe"    : "REQUIRE_MOVBE",
            "popcnt"   : "REQUIRE_POPCNT",
            "tsc_deadline_timer"   : "REQUIRE_TSC_DEADLINE_TIMER",
            "xsave"    : "REQUIRE_XSAVE",
            "avx"      : "REQUIRE_AVX",
            "f16c"     : "REQUIRE_F16C",
            "rdrand"   : "REQUIRE_RDRAND",
            "fsgsbase" : "REQUIRE_FSGSBASE",
            "bmi1"     : "REQUIRE_BMI1",
            "hle"      : "REQUIRE_HLE",
            "avx2"     : "REQUIRE_AVX2",
            "smep"     : "REQUIRE_SMEP",
            "bmi2"     : "REQUIRE_BMI2",
            "erms"     : "REQUIRE_ERMS",
            "invpcid"  : "REQUIRE_INVPCID",
            "rtm"      : "REQUIRE_RTM",
            "mpx"      : "REQUIRE_MPX",
            "rdseed"   : "REQUIRE_RDSEED",
            "adx"      : "REQUIRE_ADX",
            "smap"     : "REQUIRE_SMAP",
        }

    def mano_to_extra_spec_cpu_model(self, cpu_model):
        if cpu_model in self._mano_to_espec_cpumodel:
            return self._mano_to_espec_cpumodel[cpu_model]
        else:
            return None
            
    def extra_specs_to_mano_cpu_model(self, cpu_model):
        if cpu_model in self._espec_to_mano_cpumodel:
            return self._espec_to_mano_cpumodel[cpu_model]
        else:
            return None
        
    def mano_to_extra_spec_cpu_arch(self, cpu_arch):
        if cpu_arch in self._mano_to_espec_cpuarch:
            return self._mano_to_espec_cpuarch[cpu_arch]
        else:
            return None
        
    def extra_specs_to_mano_cpu_arch(self, cpu_arch):
        if cpu_arch in self._espec_to_mano_cpuarch:
            return self._espec_to_mano_cpuarch[cpu_arch]
        else:
            return None
    
    def mano_to_extra_spec_cpu_vendor(self, cpu_vendor):
        if cpu_vendor in self._mano_to_espec_cpuvendor:
            return self._mano_to_espec_cpuvendor[cpu_vendor]
        else:
            return None

    def extra_spec_to_mano_cpu_vendor(self, cpu_vendor):
        if cpu_vendor in self._espec_to_mano_cpuvendor:
            return self._espec_to_mano_cpuvendor[cpu_vendor]
        else:
            return None
    
    def mano_to_extra_spec_cpu_socket_count(self, cpu_sockets):
        return cpu_sockets

    def extra_spec_to_mano_cpu_socket_count(self, cpu_sockets):
        return int(cpu_sockets)
    
    def mano_to_extra_spec_cpu_core_count(self, cpu_core_count):
        return cpu_core_count

    def extra_spec_to_mano_cpu_core_count(self, cpu_core_count):
        return int(cpu_core_count)
    
    def mano_to_extra_spec_cpu_core_thread_count(self, core_thread_count):
        return core_thread_count

    def extra_spec_to_mano_cpu_core_thread_count(self, core_thread_count):
        return int(core_thread_count)

    def mano_to_extra_spec_cpu_features(self, features):
        cpu_features = []
        epa_feature_str = None
        for f in features:
            if f in self._mano_to_espec_cpufeatures:
                cpu_features.append(self._mano_to_espec_cpufeatures[f])
                
        if len(cpu_features) > 1:
            epa_feature_str =  '<all-in> '+ " ".join(cpu_features)
        elif len(cpu_features) == 1:
            epa_feature_str = " ".join(cpu_features)

        return epa_feature_str

    def extra_spec_to_mano_cpu_features(self, features):
        oper_symbols = ['=', '<in>', '<all-in>', '==', '!=', '>=', '<=', 's==', 's!=', 's<', 's<=', 's>', 's>=']
        cpu_features = []
        result = None
        for oper in oper_symbols:
            regex = '^'+oper+' (.*?)$'
            result = re.search(regex, features)
            if result is not None:
                break
            
        if result is not None:
            feature_list = result.group(1)
        else:
            feature_list = features

        for f in feature_list.split():
            if f in self._espec_to_mano_cpufeatures:
                cpu_features.append(self._espec_to_mano_cpufeatures[f])

        return cpu_features
    

class ExtraSpecUtils(object):
    """
    General utility class for flavor Extra Specs processing
    """
    def __init__(self):
        self.host = HostEPAUtils()
        self.guest = GuestEPAUtils()
        self.extra_specs_keywords = [ 'hw:cpu_policy',
                                      'hw:cpu_threads_policy',
                                      'hw:mem_page_size',
                                      'hw:numa_nodes',
                                      'hw:numa_mempolicy',
                                      'hw:numa_cpus',
                                      'hw:numa_mem',
                                      'trust:trusted_host',
                                      'pci_passthrough:alias',
                                      'capabilities:cpu_info:model',
                                      'capabilities:cpu_info:arch',
                                      'capabilities:cpu_info:vendor',
                                      'capabilities:cpu_info:topology:sockets',
                                      'capabilities:cpu_info:topology:cores',
                                      'capabilities:cpu_info:topology:threads',
                                      'capabilities:cpu_info:features',
                                ]
        self.extra_specs_regex = re.compile("^"+"|^".join(self.extra_specs_keywords))



class FlavorUtils(object):
    """
    Utility class for handling the flavor 
    """
    def __init__(self, driver):
        """
        Constructor for class
        Arguments:
          driver: object of OpenstackDriver()
        """
        self._epa = ExtraSpecUtils()
        self._driver = driver
        self.log = driver.log
        
    @property
    def driver(self):
        return self._driver
    
    def _get_guest_epa_specs(self, guest_epa):
        """
        Returns EPA Specs dictionary for guest_epa attributes
        """
        epa_specs = dict()
        if guest_epa.has_field('mempage_size'):
            mempage_size = self._epa.guest.mano_to_extra_spec_mempage_size(guest_epa.mempage_size)
            if mempage_size is not None:
                epa_specs['hw:mem_page_size'] = mempage_size

        if guest_epa.has_field('cpu_pinning_policy'):
            cpu_pinning_policy = self._epa.guest.mano_to_extra_spec_cpu_pinning_policy(guest_epa.cpu_pinning_policy)
            if cpu_pinning_policy is not None:
                epa_specs['hw:cpu_policy'] = cpu_pinning_policy

        if guest_epa.has_field('cpu_thread_pinning_policy'):
            cpu_thread_pinning_policy = self._epa.guest.mano_to_extra_spec_cpu_thread_pinning_policy(guest_epa.cpu_thread_pinning_policy)
            if cpu_thread_pinning_policy is None:
                epa_specs['hw:cpu_threads_policy'] = cpu_thread_pinning_policy

        if guest_epa.has_field('trusted_execution'):
            trusted_execution = self._epa.guest.mano_to_extra_spec_trusted_execution(guest_epa.trusted_execution)
            if trusted_execution is not None:
                epa_specs['trust:trusted_host'] = trusted_execution

        if guest_epa.has_field('numa_node_policy'):
            if guest_epa.numa_node_policy.has_field('node_cnt'):
                numa_node_count = self._epa.guest.mano_to_extra_spec_numa_node_count(guest_epa.numa_node_policy.node_cnt)
                if numa_node_count is not None:
                    epa_specs['hw:numa_nodes'] = numa_node_count

            if guest_epa.numa_node_policy.has_field('mem_policy'):
                numa_memory_policy = self._epa.guest.mano_to_extra_spec_numa_memory_policy(guest_epa.numa_node_policy.mem_policy)
                if numa_memory_policy is not None:
                    epa_specs['hw:numa_mempolicy'] = numa_memory_policy

            if guest_epa.numa_node_policy.has_field('node'):
                for node in guest_epa.numa_node_policy.node:
                    if node.has_field('vcpu') and node.vcpu:
                        epa_specs['hw:numa_cpus.'+str(node.id)] = ','.join([str(j.id) for j in node.vcpu])
                    if node.memory_mb:
                        epa_specs['hw:numa_mem.'+str(node.id)] = str(node.memory_mb)

        if guest_epa.has_field('pcie_device'):
            pci_devices = []
            for device in guest_epa.pcie_device:
                pci_devices.append(device.device_id +':'+str(device.count))
            epa_specs['pci_passthrough:alias'] = ','.join(pci_devices)

        return epa_specs

    def _get_host_epa_specs(self,host_epa):
        """
        Returns EPA Specs dictionary for host_epa attributes
        """
        epa_specs = dict()

        if host_epa.has_field('cpu_model'):
            cpu_model = self._epa.host.mano_to_extra_spec_cpu_model(host_epa.cpu_model)
            if cpu_model is not None:
                epa_specs['capabilities:cpu_info:model'] = cpu_model

        if host_epa.has_field('cpu_arch'):
            cpu_arch = self._epa.host.mano_to_extra_spec_cpu_arch(host_epa.cpu_arch)
            if cpu_arch is not None:
                epa_specs['capabilities:cpu_info:arch'] = cpu_arch

        if host_epa.has_field('cpu_vendor'):
            cpu_vendor = self._epa.host.mano_to_extra_spec_cpu_vendor(host_epa.cpu_vendor)
            if cpu_vendor is not None:
                epa_specs['capabilities:cpu_info:vendor'] = cpu_vendor

        if host_epa.has_field('cpu_socket_count'):
            cpu_socket_count = self._epa.host.mano_to_extra_spec_cpu_socket_count(host_epa.cpu_socket_count)
            if cpu_socket_count is not None:
                epa_specs['capabilities:cpu_info:topology:sockets'] = cpu_socket_count

        if host_epa.has_field('cpu_core_count'):
            cpu_core_count = self._epa.host.mano_to_extra_spec_cpu_core_count(host_epa.cpu_core_count)
            if cpu_core_count is not None:
                epa_specs['capabilities:cpu_info:topology:cores'] = cpu_core_count

        if host_epa.has_field('cpu_core_thread_count'):
            cpu_core_thread_count = self._epa.host.mano_to_extra_spec_cpu_core_thread_count(host_epa.cpu_core_thread_count)
            if cpu_core_thread_count is not None:
                epa_specs['capabilities:cpu_info:topology:threads'] = cpu_core_thread_count

        if host_epa.has_field('cpu_feature'):
            cpu_features = []
            espec_cpu_features = []
            for feature in host_epa.cpu_feature:
                cpu_features.append(feature.feature)
            espec_cpu_features = self._epa.host.mano_to_extra_spec_cpu_features(cpu_features)
            if espec_cpu_features is not None:
                epa_specs['capabilities:cpu_info:features'] = espec_cpu_features
        return epa_specs

    def _get_hypervisor_epa_specs(self,guest_epa):
        """
        Returns EPA Specs dictionary for hypervisor_epa attributes
        """
        hypervisor_epa = dict()
        return hypervisor_epa

    def _get_vswitch_epa_specs(self, guest_epa):
        """
        Returns EPA Specs dictionary for vswitch_epa attributes
        """
        vswitch_epa = dict()
        return vswitch_epa

    def _get_host_aggregate_epa_specs(self, host_aggregate):
        """
        Returns EPA Specs dictionary for host aggregates
        """
        epa_specs = dict()
        for aggregate in host_aggregate:
            epa_specs['aggregate_instance_extra_specs:'+aggregate.metadata_key] = aggregate.metadata_value

        return epa_specs
    
    def get_extra_specs(self, flavor):
        """
        Returns epa_specs dictionary based on flavor information
        Arguments
           flavor -- Protobuf GI object for flavor_info (RwcalYang.FlavorInfoItem())
        Returns:
           A dictionary of extra_specs in format understood by novaclient library
        """
        epa_specs = dict()
        if flavor.has_field('guest_epa'):
            guest_epa = self._get_guest_epa_specs(flavor.guest_epa)
            epa_specs.update(guest_epa)
        if flavor.has_field('host_epa'):
            host_epa = self._get_host_epa_specs(flavor.host_epa)
            epa_specs.update(host_epa)
        if flavor.has_field('hypervisor_epa'):
            hypervisor_epa = self._get_hypervisor_epa_specs(flavor.hypervisor_epa)
            epa_specs.update(hypervisor_epa)
        if flavor.has_field('vswitch_epa'):
            vswitch_epa = self._get_vswitch_epa_specs(flavor.vswitch_epa)
            epa_specs.update(vswitch_epa)
        if flavor.has_field('host_aggregate'):
            host_aggregate = self._get_host_aggregate_epa_specs(flavor.host_aggregate)
            epa_specs.update(host_aggregate)
        return epa_specs


    def parse_vm_flavor_epa_info(self, flavor_info):
        """
        Parse the flavor_info dictionary (returned by python-client) for vm_flavor

        Arguments:
           flavor_info: A dictionary object return by novaclient library listing flavor attributes

        Returns:
               vm_flavor = RwcalYang.FlavorInfoItem_VmFlavor()
        """
        vm_flavor = RwcalYang.FlavorInfoItem_VmFlavor()

        if 'vcpus' in flavor_info and flavor_info['vcpus']:
            vm_flavor.vcpu_count = flavor_info['vcpus']

        if 'ram' in flavor_info and flavor_info['ram']:
            vm_flavor.memory_mb  = flavor_info['ram']

        if 'disk' in flavor_info and flavor_info['disk']:
            vm_flavor.storage_gb  = flavor_info['disk']

        return vm_flavor
    
    def parse_guest_epa_info(self, flavor_info):
        """
        Parse the flavor_info dictionary (returned by python-client) for guest_epa

        Arguments:
           flavor_info: A dictionary object return by novaclient library listing flavor attributes

        Returns:
           guest_epa = RwcalYang.FlavorInfoItem_GuestEpa()
        """
        guest_epa = RwcalYang.FlavorInfoItem_GuestEpa()
        for attr in flavor_info['extra_specs']:
            if attr == 'hw:cpu_policy':
                cpu_pinning_policy = self._epa.guest.extra_spec_to_mano_cpu_pinning_policy(flavor_info['extra_specs']['hw:cpu_policy'])
                if cpu_pinning_policy is not None:
                    guest_epa.cpu_pinning_policy = cpu_pinning_policy

            elif attr == 'hw:cpu_threads_policy':
                cpu_thread_pinning_policy = self._epa.guest.extra_spec_to_mano_cpu_thread_pinning_policy(flavor_info['extra_specs']['hw:cpu_threads_policy'])
                if cpu_thread_pinning_policy is not None:
                    guest_epa.cpu_thread_pinning_policy = cpu_thread_pinning_policy

            elif attr == 'hw:mem_page_size':
                mempage_size = self._epa.guest.extra_spec_to_mano_mempage_size(flavor_info['extra_specs']['hw:mem_page_size'])
                if mempage_size is not None:
                    guest_epa.mempage_size = mempage_size

            elif attr == 'hw:numa_nodes':
                numa_node_count = self._epa.guest.extra_specs_to_mano_numa_node_count(flavor_info['extra_specs']['hw:numa_nodes'])
                if numa_node_count is not None:
                    guest_epa.numa_node_policy.node_cnt = numa_node_count

            elif attr.startswith('hw:numa_cpus.'):
                node_id = attr.split('.')[1]
                nodes = [ n for n in guest_epa.numa_node_policy.node if n.id == int(node_id) ]
                if nodes:
                    numa_node = nodes[0]
                else:
                    numa_node = guest_epa.numa_node_policy.node.add()
                    numa_node.id = int(node_id)

                for x in flavor_info['extra_specs'][attr].split(','):
                   numa_node_vcpu = numa_node.vcpu.add()
                   numa_node_vcpu.id = int(x)

            elif attr.startswith('hw:numa_mem.'):
                node_id = attr.split('.')[1]
                nodes = [ n for n in guest_epa.numa_node_policy.node if n.id == int(node_id) ]
                if nodes:
                    numa_node = nodes[0]
                else:
                    numa_node = guest_epa.numa_node_policy.node.add()
                    numa_node.id = int(node_id)

                numa_node.memory_mb =  int(flavor_info['extra_specs'][attr])

            elif attr == 'hw:numa_mempolicy':
                numa_memory_policy = self._epa.guest.extra_to_mano_spec_numa_memory_policy(flavor_info['extra_specs']['hw:numa_mempolicy'])
                if numa_memory_policy is not None:
                    guest_epa.numa_node_policy.mem_policy = numa_memory_policy

            elif attr == 'trust:trusted_host':
                trusted_execution = self._epa.guest.extra_spec_to_mano_trusted_execution(flavor_info['extra_specs']['trust:trusted_host'])
                if trusted_execution is not None:
                    guest_epa.trusted_execution = trusted_execution

            elif attr == 'pci_passthrough:alias':
                device_types = flavor_info['extra_specs']['pci_passthrough:alias']
                for device in device_types.split(','):
                    dev = guest_epa.pcie_device.add()
                    dev.device_id = device.split(':')[0]
                    dev.count = int(device.split(':')[1])
        return guest_epa

    def parse_host_epa_info(self, flavor_info):
        """
        Parse the flavor_info dictionary (returned by python-client) for host_epa

        Arguments:
           flavor_info: A dictionary object return by novaclient library listing flavor attributes

        Returns:
           host_epa  = RwcalYang.FlavorInfoItem_HostEpa()
        """
        host_epa  = RwcalYang.FlavorInfoItem_HostEpa()
        for attr in flavor_info['extra_specs']:
            if attr == 'capabilities:cpu_info:model':
                cpu_model = self._epa.host.extra_specs_to_mano_cpu_model(flavor_info['extra_specs']['capabilities:cpu_info:model'])
                if cpu_model is not None:
                    host_epa.cpu_model = cpu_model

            elif attr == 'capabilities:cpu_info:arch':
                cpu_arch = self._epa.host.extra_specs_to_mano_cpu_arch(flavor_info['extra_specs']['capabilities:cpu_info:arch'])
                if cpu_arch is not None:
                    host_epa.cpu_arch = cpu_arch

            elif attr == 'capabilities:cpu_info:vendor':
                cpu_vendor = self._epa.host.extra_spec_to_mano_cpu_vendor(flavor_info['extra_specs']['capabilities:cpu_info:vendor'])
                if cpu_vendor is not None:
                    host_epa.cpu_vendor = cpu_vendor

            elif attr == 'capabilities:cpu_info:topology:sockets':
                cpu_sockets = self._epa.host.extra_spec_to_mano_cpu_socket_count(flavor_info['extra_specs']['capabilities:cpu_info:topology:sockets'])
                if cpu_sockets is not None:
                    host_epa.cpu_socket_count = cpu_sockets

            elif attr == 'capabilities:cpu_info:topology:cores':
                cpu_cores = self._epa.host.extra_spec_to_mano_cpu_core_count(flavor_info['extra_specs']['capabilities:cpu_info:topology:cores'])
                if cpu_cores is not None:
                    host_epa.cpu_core_count = cpu_cores

            elif attr == 'capabilities:cpu_info:topology:threads':
                cpu_threads = self._epa.host.extra_spec_to_mano_cpu_core_thread_count(flavor_info['extra_specs']['capabilities:cpu_info:topology:threads'])
                if cpu_threads is not None:
                    host_epa.cpu_core_thread_count = cpu_threads

            elif attr == 'capabilities:cpu_info:features':
                cpu_features = self._epa.host.extra_spec_to_mano_cpu_features(flavor_info['extra_specs']['capabilities:cpu_info:features'])
                if cpu_features is not None:
                    for feature in cpu_features:
                        host_epa.cpu_feature.append(feature)
        return host_epa
    
    def parse_host_aggregate_epa_info(self, flavor_info):
        """
        Parse the flavor_info dictionary (returned by python-client) for host_aggregate

        Arguments:
           flavor_info: A dictionary object return by novaclient library listing flavor attributes

        Returns:
           A list of objects host_aggregate of type RwcalYang.FlavorInfoItem_HostAggregate()
        """
        host_aggregates = list()
        for attr in flavor_info['extra_specs']:
            if attr.startswith('aggregate_instance_extra_specs:'):
                aggregate = RwcalYang.FlavorInfoItem_HostAggregate()
                aggregate.metadata_key = ":".join(attr.split(':')[1::])
                aggregate.metadata_value = flavor_info['extra_specs'][attr]
                host_aggregates.append(aggregate)
        return host_aggregates
    
        
    def parse_flavor_info(self, flavor_info):
        """
        Parse the flavor_info dictionary and put value in RIFT GI object for flavor
        Arguments:
           flavor_info: A dictionary object returned by novaclient library listing flavor attributes

        Returns: 
           Protobuf GI Object of type RwcalYang.FlavorInfoItem()

        """
        flavor = RwcalYang.FlavorInfoItem()
        if 'name' in flavor_info and flavor_info['name']:
            flavor.name  = flavor_info['name']
        if 'id' in flavor_info and flavor_info['id']:
            flavor.id  = flavor_info['id']

        ### If extra_specs in flavor_info
        if 'extra_specs' in flavor_info:
            flavor.vm_flavor = self.parse_vm_flavor_epa_info(flavor_info)
            flavor.guest_epa = self.parse_guest_epa_info(flavor_info)
            flavor.host_epa = self.parse_host_epa_info(flavor_info)
            for aggr in self.parse_host_aggregate_epa_info(flavor_info):
                ha = flavor.host_aggregate.add()
                ha.from_dict(aggr.as_dict())
        return flavor

    def _match_vm_flavor(self, required, available):
        self.log.info("Matching VM Flavor attributes")
        if available.vcpu_count != required.vcpu_count:
            self.log.debug("VCPU requirement mismatch. Required: %d, Available: %d",
                           required.vcpu_count,
                           available.vcpu_count)
            return False
        if available.memory_mb != required.memory_mb:
            self.log.debug("Memory requirement mismatch. Required: %d MB, Available: %d MB",
                           required.memory_mb,
                           available.memory_mb)
            return False
        if available.storage_gb != required.storage_gb:
            self.log.debug("Storage requirement mismatch. Required: %d GB, Available: %d GB",
                           required.storage_gb,
                           available.storage_gb)
            return False
        self.log.debug("VM Flavor match found")
        return True

    def _match_guest_epa(self, required, available):
        self.log.info("Matching Guest EPA attributes")
        if required.has_field('pcie_device'):
            self.log.debug("Matching pcie_device")
            if available.has_field('pcie_device') == False:
                self.log.debug("Matching pcie_device failed. Not available in flavor")
                return False
            else:
                for dev in required.pcie_device:
                    if not [ d for d in available.pcie_device
                            if ((d.device_id == dev.device_id) and (d.count == dev.count)) ]:
                        self.log.debug("Matching pcie_device failed. Required: %s, Available: %s",
                                       required.pcie_device, available.pcie_device)
                        return False
        elif available.has_field('pcie_device'):
            self.log.debug("Rejecting available flavor because pcie_device not required but available")
            return False


        if required.has_field('mempage_size'):
            self.log.debug("Matching mempage_size")
            if available.has_field('mempage_size') == False:
                self.log.debug("Matching mempage_size failed. Not available in flavor")
                return False
            else:
                if required.mempage_size != available.mempage_size:
                    self.log.debug("Matching mempage_size failed. Required: %s, Available: %s",
                                   required.mempage_size, available.mempage_size)
                    return False
        elif available.has_field('mempage_size'):
            self.log.debug("Rejecting available flavor because mempage_size not required but available")
            return False

        if required.has_field('cpu_pinning_policy'):
            self.log.debug("Matching cpu_pinning_policy")
            if required.cpu_pinning_policy != 'ANY':
                if available.has_field('cpu_pinning_policy') == False:
                    self.log.debug("Matching cpu_pinning_policy failed. Not available in flavor")
                    return False
                else:
                    if required.cpu_pinning_policy != available.cpu_pinning_policy:
                        self.log.debug("Matching cpu_pinning_policy failed. Required: %s, Available: %s",
                                       required.cpu_pinning_policy, available.cpu_pinning_policy)
                        return False
        elif available.has_field('cpu_pinning_policy'):
            self.log.debug("Rejecting available flavor because cpu_pinning_policy not required but available")
            return False

        if required.has_field('cpu_thread_pinning_policy'):
            self.log.debug("Matching cpu_thread_pinning_policy")
            if available.has_field('cpu_thread_pinning_policy') == False:
                self.log.debug("Matching cpu_thread_pinning_policy failed. Not available in flavor")
                return False
            else:
                if required.cpu_thread_pinning_policy != available.cpu_thread_pinning_policy:
                    self.log.debug("Matching cpu_thread_pinning_policy failed. Required: %s, Available: %s",
                                   required.cpu_thread_pinning_policy, available.cpu_thread_pinning_policy)
                    return False
        elif available.has_field('cpu_thread_pinning_policy'):
            self.log.debug("Rejecting available flavor because cpu_thread_pinning_policy not required but available")
            return False

        if required.has_field('trusted_execution'):
            self.log.debug("Matching trusted_execution")
            if required.trusted_execution == True:
                if available.has_field('trusted_execution') == False:
                    self.log.debug("Matching trusted_execution failed. Not available in flavor")
                    return False
                else:
                    if required.trusted_execution != available.trusted_execution:
                        self.log.debug("Matching trusted_execution failed. Required: %s, Available: %s",
                                       required.trusted_execution, available.trusted_execution)
                        return False
        elif available.has_field('trusted_execution'):
            self.log.debug("Rejecting available flavor because trusted_execution not required but available")
            return False

        if required.has_field('numa_node_policy'):
            self.log.debug("Matching numa_node_policy")
            if available.has_field('numa_node_policy') == False:
                self.log.debug("Matching numa_node_policy failed. Not available in flavor")
                return False
            else:
                if required.numa_node_policy.has_field('node_cnt'):
                    self.log.debug("Matching numa_node_policy node_cnt")
                    if available.numa_node_policy.has_field('node_cnt') == False:
                        self.log.debug("Matching numa_node_policy node_cnt failed. Not available in flavor")
                        return False
                    else:
                        if required.numa_node_policy.node_cnt != available.numa_node_policy.node_cnt:
                            self.log.debug("Matching numa_node_policy node_cnt failed. Required: %s, Available: %s",
                                           required.numa_node_policy.node_cnt, available.numa_node_policy.node_cnt)
                            return False
                elif available.numa_node_policy.has_field('node_cnt'):
                    self.log.debug("Rejecting available flavor because numa node count not required but available")
                    return False

                if required.numa_node_policy.has_field('mem_policy'):
                    self.log.debug("Matching numa_node_policy mem_policy")
                    if available.numa_node_policy.has_field('mem_policy') == False:
                        self.log.debug("Matching numa_node_policy mem_policy failed. Not available in flavor")
                        return False
                    else:
                        if required.numa_node_policy.mem_policy != available.numa_node_policy.mem_policy:
                            self.log.debug("Matching numa_node_policy mem_policy failed. Required: %s, Available: %s",
                                           required.numa_node_policy.mem_policy, available.numa_node_policy.mem_policy)
                            return False
                elif available.numa_node_policy.has_field('mem_policy'):
                    self.log.debug("Rejecting available flavor because num node mem_policy not required but available")
                    return False

                if required.numa_node_policy.has_field('node'):
                    self.log.debug("Matching numa_node_policy nodes configuration")
                    if available.numa_node_policy.has_field('node') == False:
                        self.log.debug("Matching numa_node_policy nodes configuration failed. Not available in flavor")
                        return False
                    for required_node in required.numa_node_policy.node:
                        self.log.debug("Matching numa_node_policy nodes configuration for node %s",
                                       required_node)
                        numa_match = False
                        for available_node in available.numa_node_policy.node:
                            if required_node.id != available_node.id:
                                self.log.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s",
                                               required_node, available_node)
                                continue
                            if required_node.vcpu != available_node.vcpu:
                                self.log.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s",
                                               required_node, available_node)
                                continue
                            if required_node.memory_mb != available_node.memory_mb:
                                self.log.debug("Matching numa_node_policy nodes configuration failed. Required: %s, Available: %s",
                                               required_node, available_node)
                                continue
                            numa_match = True
                        if numa_match == False:
                            return False
                elif available.numa_node_policy.has_field('node'):
                    self.log.debug("Rejecting available flavor because numa nodes not required but available")
                    return False
        elif available.has_field('numa_node_policy'):
            self.log.debug("Rejecting available flavor because numa_node_policy not required but available")
            return False
        self.log.info("Successful match for Guest EPA attributes")
        return True

    def _match_vswitch_epa(self, required, available):
        self.log.debug("VSwitch EPA match found")
        return True

    def _match_hypervisor_epa(self, required, available):
        self.log.debug("Hypervisor EPA match found")
        return True

    def _match_host_epa(self, required, available):
        self.log.info("Matching Host EPA attributes")
        if required.has_field('cpu_model'):
            self.log.debug("Matching CPU model")
            if available.has_field('cpu_model') == False:
                self.log.debug("Matching CPU model failed. Not available in flavor")
                return False
            else:
                #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
                if required.cpu_model.replace('PREFER', 'REQUIRE') != available.cpu_model:
                    self.log.debug("Matching CPU model failed. Required: %s, Available: %s",
                                   required.cpu_model, available.cpu_model)
                    return False
        elif available.has_field('cpu_model'):
            self.log.debug("Rejecting available flavor because cpu_model not required but available")
            return False

        if required.has_field('cpu_arch'):
            self.log.debug("Matching CPU architecture")
            if available.has_field('cpu_arch') == False:
                self.log.debug("Matching CPU architecture failed. Not available in flavor")
                return False
            else:
                #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
                if required.cpu_arch.replace('PREFER', 'REQUIRE') != available.cpu_arch:
                    self.log.debug("Matching CPU architecture failed. Required: %s, Available: %s",
                                   required.cpu_arch, available.cpu_arch)
                    return False
        elif available.has_field('cpu_arch'):
            self.log.debug("Rejecting available flavor because cpu_arch not required but available")
            return False

        if required.has_field('cpu_vendor'):
            self.log.debug("Matching CPU vendor")
            if available.has_field('cpu_vendor') == False:
                self.log.debug("Matching CPU vendor failed. Not available in flavor")
                return False
            else:
                #### Convert all PREFER to REQUIRE since flavor will only have REQUIRE attributes
                if required.cpu_vendor.replace('PREFER', 'REQUIRE') != available.cpu_vendor:
                    self.log.debug("Matching CPU vendor failed. Required: %s, Available: %s",
                                   required.cpu_vendor, available.cpu_vendor)
                    return False
        elif available.has_field('cpu_vendor'):
            self.log.debug("Rejecting available flavor because cpu_vendor not required but available")
            return False

        if required.has_field('cpu_socket_count'):
            self.log.debug("Matching CPU socket count")
            if available.has_field('cpu_socket_count') == False:
                self.log.debug("Matching CPU socket count failed. Not available in flavor")
                return False
            else:
                if required.cpu_socket_count != available.cpu_socket_count:
                    self.log.debug("Matching CPU socket count failed. Required: %s, Available: %s",
                                   required.cpu_socket_count, available.cpu_socket_count)
                    return False
        elif available.has_field('cpu_socket_count'):
            self.log.debug("Rejecting available flavor because cpu_socket_count not required but available")
            return False

        if required.has_field('cpu_core_count'):
            self.log.debug("Matching CPU core count")
            if available.has_field('cpu_core_count') == False:
                self.log.debug("Matching CPU core count failed. Not available in flavor")
                return False
            else:
                if required.cpu_core_count != available.cpu_core_count:
                    self.log.debug("Matching CPU core count failed. Required: %s, Available: %s",
                                   required.cpu_core_count, available.cpu_core_count)
                    return False
        elif available.has_field('cpu_core_count'):
            self.log.debug("Rejecting available flavor because cpu_core_count not required but available")
            return False

        if required.has_field('cpu_core_thread_count'):
            self.log.debug("Matching CPU core thread count")
            if available.has_field('cpu_core_thread_count') == False:
                self.log.debug("Matching CPU core thread count failed. Not available in flavor")
                return False
            else:
                if required.cpu_core_thread_count != available.cpu_core_thread_count:
                    self.log.debug("Matching CPU core thread count failed. Required: %s, Available: %s",
                                   required.cpu_core_thread_count, available.cpu_core_thread_count)
                    return False
        elif available.has_field('cpu_core_thread_count'):
            self.log.debug("Rejecting available flavor because cpu_core_thread_count not required but available")
            return False

        if required.has_field('cpu_feature'):
            self.log.debug("Matching CPU feature list")
            if available.has_field('cpu_feature') == False:
                self.log.debug("Matching CPU feature list failed. Not available in flavor")
                return False
            else:
                for feature in required.cpu_feature:
                    if feature not in available.cpu_feature:
                        self.log.debug("Matching CPU feature list failed. Required feature: %s is not present. Available features: %s",
                                       feature, available.cpu_feature)
                        return False
        elif available.has_field('cpu_feature'):
            self.log.debug("Rejecting available flavor because cpu_feature not required but available")
            return False
        self.log.info("Successful match for Host EPA attributes")
        return True


    def _match_placement_group_inputs(self, required, available):
        self.log.info("Matching Host aggregate attributes")

        if not required and not available:
            # Host aggregate not required and not available => success
            self.log.info("Successful match for Host Aggregate attributes")
            return True
        if required and available:
            # Host aggregate requested and available => Do a match and decide
            xx = [ x.as_dict() for x in required ]
            yy = [ y.as_dict() for y in available ]
            for i in xx:
                if i not in yy:
                    self.log.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s",
                                   required, available)
                    return False
            self.log.info("Successful match for Host Aggregate attributes")
            return True
        else:
            # Either of following conditions => Failure
            #  - Host aggregate required but not available
            #  - Host aggregate not required but available
            self.log.debug("Rejecting available flavor because host Aggregate mismatch. Required: %s, Available: %s",
                           required, available)
            return False
    

    def _match_epa_params(self, resource_info, request_params):
        """
        Match EPA attributes
        Arguments:
           resource_info: Protobuf GI object RwcalYang.FlavorInfoItem()
                          Following attributes would be accessed
                          - vm_flavor
                          - guest_epa
                          - host_epa
                          - host_aggregate

           request_params: Protobuf GI object RwcalYang.VDUInitParams(). 
                          Following attributes would be accessed
                          - vm_flavor
                          - guest_epa
                          - host_epa
                          - host_aggregate
        Returns:
           True -- Match between resource_info and request_params
           False -- No match between resource_info and request_params
        """
        result = False
        result = self._match_vm_flavor(getattr(request_params, 'vm_flavor'),
                                       getattr(resource_info, 'vm_flavor'))
        if result == False:
            self.log.debug("VM Flavor mismatched")
            return False

        result = self._match_guest_epa(getattr(request_params, 'guest_epa'),
                                       getattr(resource_info, 'guest_epa'))
        if result == False:
            self.log.debug("Guest EPA mismatched")
            return False

        result = self._match_vswitch_epa(getattr(request_params, 'vswitch_epa'),
                                         getattr(resource_info, 'vswitch_epa'))
        if result == False:
            self.log.debug("Vswitch EPA mismatched")
            return False

        result = self._match_hypervisor_epa(getattr(request_params, 'hypervisor_epa'),
                                            getattr(resource_info, 'hypervisor_epa'))
        if result == False:
            self.log.debug("Hypervisor EPA mismatched")
            return False

        result = self._match_host_epa(getattr(request_params, 'host_epa'),
                                      getattr(resource_info, 'host_epa'))
        if result == False:
            self.log.debug("Host EPA mismatched")
            return False

        result = self._match_placement_group_inputs(getattr(request_params, 'host_aggregate'),
                                                    getattr(resource_info, 'host_aggregate'))

        if result == False:
            self.log.debug("Host Aggregate mismatched")
            return False

        return True

    def match_resource_flavor(self, vdu_init, flavor_list):
        """
        Arguments:
           vdu_init: Protobuf GI object RwcalYang.VDUInitParams(). 
           flavor_list: List of Protobuf GI object RwcalYang.FlavorInfoItem()

        Returns:
           Flavor_ID -- If match is found between vdu_init and one of flavor_info from flavor_list
           None -- No match between vdu_init and one of flavor_info from flavor_list

        Select a existing flavor if it matches the request or create new flavor
        """
        for flv in flavor_list:
            self.log.info("Attempting to match compute requirement for VDU: %s with flavor %s",
                          vdu_init.name, flv)
            if self._match_epa_params(flv, vdu_init):
                self.log.info("Flavor match found for compute requirements for VDU: %s with flavor name: %s, flavor-id: %s",
                              vdu_init.name, flv.name, flv.id)
                return flv.id
        return None

