예제 #1
0
class PhysicalDomain(model_base.Base, model_base.HasDisplayName,
                     model_base.HasAimId, model_base.AttributeMixin,
                     model_base.IsMonitored, model_base.HasName):
    """DB model for VMM Domain."""
    __tablename__ = 'aim_physical_domains'
    __table_args__ = (model_base.uniq_column(__tablename__, 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))
class DeviceClusterInterface(model_base.Base, model_base.HasAimId,
                             model_base.HasName, model_base.HasDisplayName,
                             model_base.HasTenantName,
                             model_base.AttributeMixin,
                             model_base.IsMonitored):
    """DB model for DeviceClusterInterface."""

    __tablename__ = 'aim_device_cluster_ifs'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'device_cluster_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    device_cluster_name = model_base.name_column(nullable=False)
    encap = sa.Column(sa.String(24))

    concrete_ifs = orm.relationship(DeviceClusterInterfaceConcreteIfs,
                                    backref='cluster_interface',
                                    cascade='all, delete-orphan',
                                    lazy='joined')

    def from_attr(self, session, res_attr):
        if 'concrete_interfaces' in res_attr:
            ifs = []
            for i in (res_attr.pop('concrete_interfaces', []) or []):
                ifs.append(DeviceClusterInterfaceConcreteIfs(interface=i))
            self.concrete_ifs = ifs
        # map remaining attributes to model
        super(DeviceClusterInterface, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(DeviceClusterInterface, self).to_attr(session)
        for f in res_attr.pop('concrete_ifs', []):
            res_attr.setdefault('concrete_interfaces', []).append(f.interface)
        return res_attr
예제 #3
0
class Tenant(model_base.Base, model_base.HasDisplayName, model_base.HasAimId,
             model_base.HasDescription, model_base.AttributeMixin,
             model_base.IsMonitored, model_base.HasName):
    """DB model for Tenant."""

    __tablename__ = 'aim_tenants'
    __table_args__ = (model_base.uniq_column(__tablename__, 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))
예제 #4
0
class VMMPolicy(model_base.Base, model_base.HasDisplayName,
                model_base.HasAimId, model_base.AttributeMixin,
                model_base.IsMonitored):
    """DB model for VMM Domain."""
    __tablename__ = 'aim_vmm_policies'
    __table_args__ = (model_base.uniq_column(__tablename__, 'type') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    type = sa.Column(sa.String(64))
예제 #5
0
class SecurityGroup(model_base.Base, model_base.HasAimId, model_base.HasName,
                    model_base.HasDisplayName, model_base.HasTenantName,
                    model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for SecurityGroup."""

    __tablename__ = 'aim_security_groups'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))
예제 #6
0
class Filter(model_base.Base, model_base.HasAimId, model_base.HasName,
             model_base.HasDisplayName, model_base.HasTenantName,
             model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for Filter."""

    __tablename__ = 'aim_filters'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))
예제 #7
0
class ApplicationProfile(model_base.Base, model_base.HasAimId,
                         model_base.HasName, model_base.HasDisplayName,
                         model_base.HasTenantName, model_base.AttributeMixin,
                         model_base.IsMonitored):
    """DB model for ApplicationProfile."""

    __tablename__ = 'aim_app_profiles'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))
예제 #8
0
class Contract(model_base.Base, model_base.HasAimId, model_base.HasName,
               model_base.HasDisplayName, model_base.HasTenantName,
               model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for Contract."""

    __tablename__ = 'aim_contracts'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    scope = sa.Column(sa.String(24))
예제 #9
0
class SecurityGroupSubject(model_base.Base, model_base.HasAimId,
                           model_base.HasName, model_base.HasDisplayName,
                           model_base.HasTenantName, model_base.AttributeMixin,
                           model_base.IsMonitored):
    """DB model SecurityGroup Subject."""
    __tablename__ = 'aim_security_group_subjects'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'security_group_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    security_group_name = model_base.name_column(nullable=False)
class ConcreteDevice(model_base.Base, model_base.HasAimId, model_base.HasName,
                     model_base.HasDisplayName, model_base.HasTenantName,
                     model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for ConcreteDevice."""

    __tablename__ = 'aim_concrete_devices'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'device_cluster_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    device_cluster_name = model_base.name_column(nullable=False)
예제 #11
0
class VRF(model_base.Base, model_base.HasAimId, model_base.HasName,
          model_base.HasDisplayName, model_base.HasTenantName,
          model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for BridgeDomain."""

    __tablename__ = 'aim_vrfs'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    policy_enforcement_pref = sa.Column(sa.String(16))
예제 #12
0
class L3Outside(model_base.Base, model_base.HasAimId, model_base.HasName,
                model_base.HasDisplayName, model_base.HasTenantName,
                model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for L3Outside."""

    __tablename__ = 'aim_l3outsides'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    vrf_name = model_base.name_column()
    l3_domain_dn = sa.Column(sa.String(1024))
예제 #13
0
class L3OutNodeProfile(model_base.Base, model_base.HasAimId,
                       model_base.HasName, model_base.HasDisplayName,
                       model_base.HasTenantName, model_base.AttributeMixin,
                       model_base.IsMonitored):
    """DB model for L3OutNodeProfile."""

    __tablename__ = 'aim_l3out_node_profiles'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'l3out_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    l3out_name = model_base.name_column(nullable=False)
예제 #14
0
class ContractSubject(model_base.Base, model_base.HasAimId, model_base.HasName,
                      model_base.HasDisplayName, model_base.HasTenantName,
                      model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for Contract Subject."""

    __tablename__ = 'aim_contract_subjects'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'contract_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    contract_name = model_base.name_column(nullable=False)
    service_graph_name = model_base.name_column()
    in_service_graph_name = model_base.name_column()
    out_service_graph_name = model_base.name_column()

    filters = orm.relationship(ContractSubjectFilter,
                               backref='contract',
                               cascade='all, delete-orphan',
                               lazy='joined')

    def from_attr(self, session, res_attr):
        ins = [f for f in self.filters if f.direction == 'in']
        outs = [f for f in self.filters if f.direction == 'out']
        bis = [f for f in self.filters if f.direction == 'bi']

        if 'in_filters' in res_attr:
            ins = []
            for f in (res_attr.pop('in_filters', []) or []):
                ins.append(ContractSubjectFilter(name=f, direction='in'))
        if 'out_filters' in res_attr:
            outs = []
            for f in (res_attr.pop('out_filters', []) or []):
                outs.append(ContractSubjectFilter(name=f, direction='out'))
        if 'bi_filters' in res_attr:
            bis = []
            for f in (res_attr.pop('bi_filters', []) or []):
                bis.append(ContractSubjectFilter(name=f, direction='bi'))
        self.filters = ins + outs + bis
        # map remaining attributes to model
        super(ContractSubject, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(ContractSubject, self).to_attr(session)
        for f in res_attr.pop('filters', []):
            if f.direction == 'in':
                attr = 'in_filters'
            elif f.direction == 'out':
                attr = 'out_filters'
            else:
                attr = 'bi_filters'
            res_attr.setdefault(attr, []).append(f.name)
        return res_attr
예제 #15
0
class SecurityGroupRule(model_base.Base, model_base.HasAimId,
                        model_base.HasName, model_base.HasDisplayName,
                        model_base.HasTenantName, model_base.AttributeMixin,
                        model_base.IsMonitored):
    """DB model SecurityGroup Subject."""
    __tablename__ = 'aim_security_group_rules'
    __table_args__ = (model_base.uniq_column(
        __tablename__, 'tenant_name', 'security_group_name',
        'security_group_subject_name', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))
    security_group_name = model_base.name_column(nullable=False)
    security_group_subject_name = model_base.name_column(nullable=False)
    remote_ips = orm.relationship(SecurityGroupRuleRemoteIp,
                                  backref='security_group_rule',
                                  cascade='all, delete-orphan',
                                  lazy='joined')
    direction = sa.Column(sa.String(16))
    ethertype = sa.Column(sa.String(16))
    ip_protocol = sa.Column(sa.String(16))
    from_port = sa.Column(sa.String(16))
    to_port = sa.Column(sa.String(16))
    conn_track = sa.Column(sa.String(25))
    icmp_code = sa.Column(sa.String(16))
    icmp_type = sa.Column(sa.String(16))
    remote_group_id = sa.Column(sa.String(64), nullable=False)

    def from_attr(self, session, res_attr):
        if 'remote_ips' in res_attr:
            # list of IPs has same order as DB objects
            old_ip_list = [x.cidr for x in self.remote_ips]
            # Use sets to calculate additions and deletions
            old_set = set(old_ip_list)
            new_set = set(res_attr['remote_ips'])
            # For deletions, start from the end of the list to preserve order
            deletion_indexes = [
                old_ip_list.index(ip) for ip in (old_set - new_set)
            ]
            deletion_indexes.sort()
            if deletion_indexes:
                for index in deletion_indexes[::-1]:
                    self.remote_ips.pop(index)
            for ip in (new_set - old_set):
                self.remote_ips.append(SecurityGroupRuleRemoteIp(cidr=ip))
            res_attr.pop('remote_ips')

        # map remaining attributes to model
        super(SecurityGroupRule, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(SecurityGroupRule, self).to_attr(session)
        res_attr['remote_ips'] = [x.cidr for x in res_attr['remote_ips']]
        return res_attr
예제 #16
0
class ActionLog(model_base.Base, model_base.AttributeMixin):
    __tablename__ = 'aim_action_logs'
    __table_args__ = (model_base.uniq_column(__tablename__, 'uuid') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    id = sa.Column(sa.BigInteger().with_variant(sa.Integer(), 'sqlite'),
                   primary_key=True)
    uuid = sa.Column(sa.Integer)
    root_rn = sa.Column(sa.String(64), nullable=False)
    action = sa.Column(sa.String(25), nullable=False)
    object_type = sa.Column(sa.String(50), nullable=False)
    object_dict = sa.Column(sa.LargeBinary(length=2**24), nullable=False)
    timestamp = sa.Column(sa.TIMESTAMP, server_default=func.now())
예제 #17
0
class VmmInjectedNamespace(model_base.Base, model_base.HasAimId,
                           model_base.HasName, model_base.HasDisplayName,
                           model_base.AttributeMixin):
    """DB model VmmInjectedNamespace."""
    __tablename__ = 'aim_vmm_inj_namespaces'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'domain_type', 'domain_name',
                               'controller_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    domain_type = model_base.name_column(nullable=False)
    domain_name = model_base.name_column(nullable=False)
    controller_name = model_base.name_column(nullable=False)
class Status(model_base.Base, model_base.HasId, model_base.AttributeMixin):
    """Represents agents running in aim deployments."""

    __tablename__ = 'aim_statuses'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'resource_type', 'resource_id') +
        model_base.to_tuple(model_base.Base.__table_args__))

    resource_type = sa.Column(sa.String(255), nullable=False)
    resource_id = sa.Column(sa.Integer, nullable=False)
    resource_root = model_base.name_column(nullable=False)
    sync_status = sa.Column(sa.String(50), nullable=True)
    sync_message = sa.Column(sa.TEXT, default='')
    health_score = sa.Column(sa.Integer, nullable=False)
예제 #19
0
class Subnet(model_base.Base, model_base.HasAimId, model_base.HasDisplayName,
             model_base.HasTenantName, model_base.AttributeMixin,
             model_base.IsMonitored):
    """DB model for Subnet."""

    __tablename__ = 'aim_subnets'
    __table_args__ = (model_base.uniq_column(__tablename__, 'tenant_name',
                                             'bd_name', 'gw_ip_mask') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    bd_name = model_base.name_column(nullable=False)
    gw_ip_mask = sa.Column(sa.String(64), nullable=False)

    scope = sa.Column(sa.String(16))
class ServiceGraphLinearChainNode(model_base.Base):
    """DB model for linear-chain nodes used by ServiceGraph."""

    __tablename__ = 'aim_service_graph_linear_chain_nodes'
    __table_args__ = (model_base.uniq_column(__tablename__, 'sg_aim_id',
                                             'name', 'sequence_number') +
                      model_base.to_tuple(model_base.Base.__table_args__))
    sg_aim_id = sa.Column(sa.Integer,
                          sa.ForeignKey('aim_service_graphs.aim_id'),
                          primary_key=True)
    name = model_base.name_column(primary_key=True)
    device_cluster_name = model_base.name_column()
    device_cluster_tenant_name = model_base.name_column()
    sequence_number = sa.Column(sa.Integer)
예제 #21
0
class ExternalSubnet(model_base.Base, model_base.HasAimId,
                     model_base.HasDisplayName, model_base.HasTenantName,
                     model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for ExternalSubnet."""

    __tablename__ = 'aim_external_subnets'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'l3out_name',
                               'external_network_name', 'cidr') +
        model_base.to_tuple(model_base.Base.__table_args__))

    l3out_name = model_base.name_column(nullable=False)
    external_network_name = model_base.name_column(nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)
예제 #22
0
class VmmInjectedDeployment(model_base.Base, model_base.HasAimId,
                            model_base.HasName, model_base.HasDisplayName,
                            model_base.AttributeMixin):
    """DB model VmmInjectedDeployment."""
    __tablename__ = 'aim_vmm_inj_deployments'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'domain_type', 'domain_name',
                               'controller_name', 'namespace_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    domain_type = model_base.name_column(nullable=False)
    domain_name = model_base.name_column(nullable=False)
    controller_name = model_base.name_column(nullable=False)
    namespace_name = model_base.name_column(nullable=False)

    replicas = sa.Column(sa.Integer)
예제 #23
0
class L3OutNode(model_base.Base, model_base.HasAimId, model_base.HasTenantName,
                model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for L3OutNode."""

    __tablename__ = 'aim_l3out_nodes'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'l3out_name',
                               'node_profile_name', 'node_path') +
        model_base.to_tuple(model_base.Base.__table_args__))

    l3out_name = model_base.name_column(nullable=False)
    node_profile_name = model_base.name_column(nullable=False)
    # Use VARCHAR with ASCII encoding to work-around MySQL limitations
    # on the length of primary keys
    node_path = sa.Column(VARCHAR(512, charset='latin1'), nullable=False)
    router_id = sa.Column(sa.String(64), nullable=False)
예제 #24
0
class VmmInjectedHost(model_base.Base, model_base.HasAimId, model_base.HasName,
                      model_base.HasDisplayName, model_base.AttributeMixin):
    """DB model VmmInjectedHost."""
    __tablename__ = 'aim_vmm_inj_hosts'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'domain_type', 'domain_name',
                               'controller_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    domain_type = model_base.name_column(nullable=False)
    domain_name = model_base.name_column(nullable=False)
    controller_name = model_base.name_column(nullable=False)

    host_name = sa.Column(sa.String(128))
    kernel_version = sa.Column(sa.String(32))
    os = sa.Column(sa.String(64))
class ServiceRedirectPolicy(model_base.Base, model_base.HasAimId,
                            model_base.HasName, model_base.HasDisplayName,
                            model_base.HasTenantName,
                            model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for ServiceRedirectPolicy."""

    __tablename__ = 'aim_service_redirect_policies'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    monitoring_policy_tenant_name = model_base.name_column()
    monitoring_policy_name = model_base.name_column()
    dest = orm.relationship(ServiceRedirectPolicyDestination,
                            backref='redirect_policy',
                            cascade='all, delete-orphan',
                            lazy='joined')

    def from_attr(self, session, res_attr):
        if 'destinations' in res_attr:
            dests = []
            for d in (res_attr.pop('destinations', []) or []):
                if not d.get('ip'):
                    continue
                dests.append(
                    ServiceRedirectPolicyDestination(
                        ip=d['ip'],
                        mac=d.get('mac'),
                        redirect_health_group_dn=d.get(
                            'redirect_health_group_dn'),
                        name=d.get('name')))
            self.dest = dests
        # map remaining attributes to model
        super(ServiceRedirectPolicy, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(ServiceRedirectPolicy, self).to_attr(session)
        for d in res_attr.pop('dest', []):
            dst = {'ip': d.ip}
            if d.mac is not None:
                dst['mac'] = d.mac
            if d.redirect_health_group_dn is not None:
                dst['redirect_health_group_dn'] = (d.redirect_health_group_dn)
            if d.name is not None:
                dst['name'] = d.name
            res_attr.setdefault('destinations', []).append(dst)
        return res_attr
예제 #26
0
class OpflexDevice(model_base.Base, model_base.AttributeMixin,
                   model_base.HasAimId):
    __tablename__ = 'aim_opflex_devices'
    __table_args__ = (model_base.uniq_column(
        __tablename__, 'pod_id', 'node_id', 'bridge_interface', 'dev_id') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    pod_id = sa.Column(sa.String(36))
    node_id = sa.Column(sa.String(36))
    bridge_interface = sa.Column(sa.String(36))
    dev_id = sa.Column(sa.String(36))

    host_name = sa.Column(sa.String(128))
    ip = sa.Column(sa.String(64))
    fabric_path_dn = sa.Column(sa.String(512))
    domain_name = sa.Column(sa.String(64))
    controller_name = sa.Column(sa.String(64))
class ConcreteDeviceInterface(model_base.Base, model_base.HasAimId,
                              model_base.HasName, model_base.HasDisplayName,
                              model_base.HasTenantName,
                              model_base.AttributeMixin,
                              model_base.IsMonitored):
    """DB model for ConcreteDeviceInterface."""

    __tablename__ = 'aim_concrete_device_ifs'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name',
                               'device_cluster_name', 'device_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    device_cluster_name = model_base.name_column(nullable=False)
    device_name = model_base.name_column(nullable=False)
    path = sa.Column(sa.String(512))
    host = sa.Column(sa.String(512), nullable=True, index=True)
예제 #28
0
class L3OutStaticRoute(model_base.Base, model_base.HasAimId,
                       model_base.HasDisplayName, model_base.HasTenantName,
                       model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for L3OutStaticRoute."""

    __tablename__ = 'aim_l3out_static_routes'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'l3out_name',
                               'node_profile_name', 'node_path', 'cidr') +
        model_base.to_tuple(model_base.Base.__table_args__))

    l3out_name = model_base.name_column(nullable=False)
    node_profile_name = model_base.name_column(nullable=False)
    # Use VARCHAR with ASCII encoding to work-around MySQL limitations
    # on the length of primary keys
    node_path = sa.Column(VARCHAR(512, charset='latin1'), nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)
    preference = sa.Column(sa.String(16), nullable=False)
    next_hop_list = orm.relationship(L3OutNextHop,
                                     backref='static_route',
                                     cascade='all, delete-orphan',
                                     lazy='joined')

    def from_attr(self, session, res_attr):
        if 'next_hop_list' in res_attr:
            next_hop_list = []
            for p in (res_attr.pop('next_hop_list', []) or []):
                if p.get('addr') and p.get('preference'):
                    next_hop_list.append(
                        L3OutNextHop(addr=p['addr'],
                                     preference=p['preference']))
            self.next_hop_list = next_hop_list

        # map remaining attributes to model
        super(L3OutStaticRoute, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(L3OutStaticRoute, self).to_attr(session)
        for p in res_attr.pop('next_hop_list', []):
            res_attr.setdefault('next_hop_list', []).append({
                'addr':
                p.addr,
                'preference':
                p.preference
            })
        return res_attr
예제 #29
0
class VMMDomain(model_base.Base, model_base.HasDisplayName,
                model_base.HasAimId, model_base.AttributeMixin,
                model_base.IsMonitored, model_base.HasName):
    """DB model for VMM Domain."""
    __tablename__ = 'aim_vmm_domains'
    __table_args__ = (model_base.uniq_column(__tablename__, 'type', 'name') +
                      model_base.to_tuple(model_base.Base.__table_args__))

    type = sa.Column(sa.String(64))
    enforcement_pref = sa.Column(sa.Enum('sw', 'hw', 'unknown'))
    mode = sa.Column(sa.Enum('default', 'n1kv', 'unknown', 'ovs', 'k8s'))
    mcast_address = sa.Column(sa.String(64))
    encap_mode = sa.Column(sa.Enum('unknown', 'vlan', 'vxlan'))
    pref_encap_mode = sa.Column(sa.Enum('unspecified', 'vlan', 'vxlan'))
    vlan_pool_name = model_base.name_column()
    vlan_pool_type = sa.Column(sa.Enum('static', 'dynamic'))
    mcast_addr_pool_name = model_base.name_column()
class DeviceCluster(model_base.Base, model_base.HasAimId, model_base.HasName,
                    model_base.HasDisplayName, model_base.HasTenantName,
                    model_base.AttributeMixin, model_base.IsMonitored):
    """DB model for DeviceCluster."""

    __tablename__ = 'aim_device_clusters'
    __table_args__ = (
        model_base.uniq_column(__tablename__, 'tenant_name', 'name') +
        model_base.to_tuple(model_base.Base.__table_args__))

    device_type = sa.Column(sa.Enum("PHYSICAL", "VIRTUAL"))
    service_type = sa.Column(sa.Enum("ADC", "FW", "OTHERS", "IDSIPS", "COPY"))
    context_aware = sa.Column(sa.Enum("single-Context", "multi-Context"))
    managed = sa.Column(sa.Boolean)
    physical_domain_name = model_base.name_column()
    vmm_domain_type = model_base.name_column()
    vmm_domain_name = model_base.name_column()
    encap = sa.Column(sa.String(24))
    devices = orm.relationship(DeviceClusterDevice,
                               backref='cluster',
                               cascade='all, delete-orphan',
                               lazy='joined')

    def from_attr(self, session, res_attr):
        if 'devices' in res_attr:
            devs = []
            for d in (res_attr.pop('devices', []) or []):
                devs.append(
                    DeviceClusterDevice(name=d['name'],
                                        path=d.get('path', None),
                                        host=d.get('host', None)))
            self.devices = devs
        # map remaining attributes to model
        super(DeviceCluster, self).from_attr(session, res_attr)

    def to_attr(self, session):
        res_attr = super(DeviceCluster, self).to_attr(session)
        for f in res_attr.pop('devices', []):
            d = {'name': f.name}
            if f.path is not None:
                d['path'] = f.path
            if f.host is not None:
                d['host'] = f.host
            res_attr.setdefault('devices', []).append(d)
        return res_attr