aboutsummaryrefslogtreecommitdiffstats
path: root/ice_validator/tests/test_vm_class_has_unique_type.py
blob: b158f5bbe6e13cec2c721a247188195ef9496cd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# -*- coding: utf8 -*-
# ============LICENSE_START====================================================
# org.onap.vvp/validation-scripts
# ===================================================================
# Copyright © 2019 AT&T Intellectual Property. All rights reserved.
# ===================================================================
#
# Unless otherwise specified, all software contained herein is licensed
# under the Apache License, Version 2.0 (the "License");
# you may not use this software 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.
#
#
#
# Unless otherwise specified, all documentation contained herein is licensed
# under the Creative Commons License, Attribution 4.0 Intl. (the "License");
# you may not use this documentation except in compliance with the License.
# You may obtain a copy of the License at
#
#             https://creativecommons.org/licenses/by/4.0/
#
# Unless required by applicable law or agreed to in writing, documentation
# 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.
#
# ============LICENSE_END============================================
#
#

"""heat parameters
"""

import collections

import pytest

from .structures import CinderVolumeAttachmentProcessor
from .structures import NovaServerProcessor
from .structures import get_all_resources
from .helpers import validates

VERSION = "2.0.0"


class VmClassValidator(object):
    """validate VM class has unique type
    """

    def __init__(self):
        self.vm_counts = None
        self.vm_classes = None
        self.vm_rids = None
        self.vm_types = None
        self.va_count = None

    def __call__(self, resources):
        """return (possibly empty) list of error message strings
        """
        if not resources:
            pytest.skip("No resources found")
        self.vm_counts = collections.defaultdict(set)
        self.vm_classes = collections.defaultdict(set)
        self.vm_rids = collections.defaultdict(set)
        self.vm_types = collections.defaultdict(set)
        va_config, self.va_count = CinderVolumeAttachmentProcessor.get_config(resources)
        if not va_config:
            pytest.skip("No Cinder Volume Attachment configurations found")
        for rid, resource in resources.items():
            vm_class = NovaServerProcessor.get_vm_class(resource)
            if vm_class:
                vm_class["cinder_volume_attachment"] = va_config.get(rid)
                match = NovaServerProcessor.get_rid_match_tuple(rid)[1]
                if match:
                    vm_type = match.groupdict().get("vm_type")
                    if vm_type:
                        self.vm_classes[vm_class].add(rid)
                        self.vm_types[vm_type].add(vm_class)
                        self.vm_counts[vm_type].add(self.va_count.get(rid))
                        self.vm_rids[vm_type].add(rid)
        if not self.vm_classes:
            pytest.skip("No vm_classes found")
        return self.get_errors()

    def get_errors(self):
        """return (possibly empty) list of error message strings
        """
        errors = []
        for k, v in self.vm_types.items():
            if len(v) > 1:
                errors.append(
                    "vm-type %s has class conflict %s"
                    % (k, ", ".join(str(list(self.vm_classes[c])) for c in v))
                )
                classes = list(v)
                errors.append(
                    "Differences %s"
                    % ", ".join([str(key_diff(classes[0], c)) for c in classes[1:]])
                )
        for k, v in self.vm_counts.items():
            if len(v) > 1:
                errors.append(
                    "Attachment count conflict %s"
                    % ({rid: self.va_count.get(rid) for rid in self.vm_rids[k]})
                )
        return errors


def key_diff(d1, d2, prefix=""):
    """Return list of keys which differ between d1 and d2 (dicts)
    """
    diff = [prefix + k for k in d1 if k not in d2]
    diff.extend(prefix + k for k in d2 if k not in d1)
    if isinstance(d1, dict) and isinstance(d2, dict):
        for k, v1 in d1.items():
            if k in d2 and v1 != d2[k]:
                v2 = d2[k]
                if isinstance(v1, type(v2)) and isinstance(v1, (dict, frozenset)):
                    diff.extend(key_diff(v1, v2, prefix=prefix + k + "."))
                else:
                    diff.append(prefix + k)
    return diff


@validates("R-01455")
def test_vm_class_has_unique_type(yaml_files):
    """
    When a VNF’s Heat Orchestration Template creates a Virtual
    Machine (i.e., OS::Nova::Server), each “class” of VMs MUST be
    assigned a VNF unique vm-type; where “class” defines VMs that
    MUST have the following identical characteristics:

    1.  OS::Nova::Server resource property flavor value
    2.  OS::Nova::Server resource property image value
    3.  Cinder Volume attachments
        Each VM in the “class” MUST have the identical Cinder
        Volume configuration
    4.  Network attachments and IP address requirements
        Each VM in the “class” MUST have the the identical number of
        ports connecting to the identical networks and requiring the
        identical IP address configuration
    """
    resources = get_all_resources(yaml_files)
    errors = VmClassValidator()(resources)
    assert not errors, "\n".join(errors)