Test Failed
Push — master ( 8ecc8b...fbff38 )
by P.R.
01:31
created

Node.set_username()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
1
"""
2
Enarksh
3
4
Copyright 2015-2016 Set Based IT Consultancy
5
6
Licence MIT
7
"""
8
import abc
9
from xml.etree.ElementTree import SubElement
10
11
from enarksh_lib.xml_generator.port.InputPort import InputPort
12
from enarksh_lib.xml_generator.port.OutputPort import OutputPort
13
14
15
class Node:
16
    """
17
    Class for generating XML messages for elements of type 'NodeType'.
18
    """
19
20
    # ------------------------------------------------------------------------------------------------------------------
21
    ALL_PORT_NAME = 'all'
22
    """
23
    Token for 'all' input or output ports on a node.
24
25
    :type: str
26
    """
27
28
    NODE_SELF_NAME = '.'
29
    """
30
    Token for node self.
31
32
    :type: str
33
    """
34
35
    NODE_ALL_NAME = '*'
36
    """
37
    Token for all child nodes.
38
39
    :type: str
40
    """
41
42
    # ------------------------------------------------------------------------------------------------------------------
43
    def __init__(self, name):
44
        """
45
        Object constructor.
46
47
        :param str name: The name of the node.
48
        """
49
        self.name = name
50
        """
51
        The name of the node.
52
53
        :type: str
54
        """
55
56
        self.child_nodes = []
57
        """
58
        The child nodes of this node.
59
60
        :type: list[enarksh_lib.xml_generator.node.Node.Node]
61
        """
62
63
        self.consumptions = []
64
        """
65
        The consumptions.
66
67
        :type: list[enarksh_lib.xml_generator.consumption.Consumption.Consumption]
68
        """
69
70
        self.input_ports = []
71
        """
72
        The input ports of this node.
73
74
        :type: list[enarksh_lib.xml_generator.port.InputPort.InputPort]
75
        """
76
77
        self.output_ports = []
78
        """
79
        The output ports of this node.
80
81
        :type: list[enarksh_lib.xml_generator.port.OutputPort.OutputPort]
82
        """
83
84
        self.parent = None
85
        """
86
        The parent node of this node.
87
88
        :type: enarksh_lib.xml_generator.node.Node.Node
89
        """
90
91
        self.resources = []
92
        """
93
        The resources of this node.
94
95
        :type: list[enarksh_lib.xml_generator.resource.Resource.Resource]
96
        """
97
98
        self.username = ''
99
        """
100
        The user under which this node or its child nodes must run.
101
102
        :type: str
103
        """
104
105
    # ------------------------------------------------------------------------------------------------------------------
106
    def add_child_node(self, child_node):
107
        """
108
        Adds a node as a child node of this node.
109
110
        :param enarksh_lib.xml_generator.node.Node.Node child_node: The new child node.
111
        """
112
        # -- @todo Test node exists.
113
        # -- @todo Test node is not self.
114
        # -- @todo Test parent node is not set.
115
116
        self.child_nodes.append(child_node)
117
        child_node.parent = self
118
119
    # ------------------------------------------------------------------------------------------------------------------
120
    def add_dependency(self, successor_node_name, successor_port_name, predecessor_node_name, predecessor_port_name):
121
        """
122
        Adds a dependency between two child nodes are this node and a child node.
123
124
        :param str successor_node_name: The successor node (use NODE_SELF_NAME for the this node).
125
        :param str successor_port_name: The successor port.
126
        :param str predecessor_node_name: The predecessor node (use NODE_SELF_NAME for the this node).
127
        :param str predecessor_port_name: The predecessor port.
128
        """
129
        if not predecessor_port_name:
130
            predecessor_port_name = self.ALL_PORT_NAME
131
        if not successor_port_name:
132
            successor_port_name = self.ALL_PORT_NAME
133
134
        if successor_node_name == self.NODE_SELF_NAME:
135
            succ_port = self.get_output_port(successor_port_name)
136
        else:
137
            succ_node = self.get_child_node(successor_node_name)
138
            succ_port = succ_node.get_input_port(successor_port_name)
139
140
        if predecessor_node_name == '.':
141
            pred_port = self.get_input_port(predecessor_port_name)
142
        else:
143
            pred_node = self.get_child_node(predecessor_node_name)
144
            pred_port = pred_node.get_output_port(predecessor_port_name)
145
146
        succ_port.add_dependency(pred_port)
147
148
    # ------------------------------------------------------------------------------------------------------------------
149
    def add_dependency_all_input_ports(self):
150
        """
151
        Add dependencies between the 'all' input port of this node and the 'all' input port of all this child nodes.
152
        """
153
        parent_port = self.get_input_port(self.ALL_PORT_NAME)
154
155
        for node in self.child_nodes:
156
            child_port = node.get_input_port(self.ALL_PORT_NAME)
157
            child_port.add_dependency(parent_port)
158
159
    # ------------------------------------------------------------------------------------------------------------------
160
    def add_dependency_all_output_ports(self):
161
        """
162
        Add dependencies between the 'all' output port of this node and the 'all' output of all this child nodes.
163
        """
164
        parent_port = self.get_output_port(self.ALL_PORT_NAME)
165
166
        for node in self.child_nodes:
167
            child_port = node.get_output_port(self.ALL_PORT_NAME)
168
            parent_port.add_dependency(child_port)
169
170
    # ------------------------------------------------------------------------------------------------------------------
171
    def finalize(self):
172
        """
173
        Ensures that all required dependencies between the 'all' input and output ports are present and removes
174
        redundant dependencies between ports and nodes.
175
        """
176
        self.ensure_dependencies()
177
        self.purge()
178
179
    # ------------------------------------------------------------------------------------------------------------------
180
    @abc.abstractmethod
181
    def generate_xml(self, parent):
182
        """
183
        Generates the XML element for this node.
184
185
        :param xml.etree.ElementTree.Element parent: The parent XML element.
186
187
        :rtype: None
188
        """
189
        raise NotImplementedError()
190
191
    # ------------------------------------------------------------------------------------------------------------------
192
    def _generate_xml_common(self, parent):
193
        """
194
        Generates the common XML elements of the XML element for this  node.
195
196
        :param xml.etree.ElementTree.Element parent: The parent XML element (i.e. the node XML element).
197
        """
198
        # Generate XML for the node name.
199
        node_name = SubElement(parent, 'NodeName')
200
        node_name.text = self.name
201
202
        # Generate XML for username.
203
        if self.username:
204
            username = SubElement(parent, 'UserName')
205
            username.text = self.username
206
207
        # Generate XML for input ports.
208
        if self.input_ports:
209
            input_ports = SubElement(parent, 'InputPorts')
210
            for port in self.input_ports:
211
                port.generate_xml(input_ports)
212
213
        # Generate XML for resources.
214
        if self.resources:
215
            resources = SubElement(parent, 'Resources')
216
            for resource in self.resources:
217
                resource.generate_xml(resources)
218
219
        # Generate XML for consumptions.
220
        if self.consumptions:
221
            consumptions = SubElement(parent, 'Consumptions')
222
            for consumption in self.consumptions:
223
                consumption.generate_xml(consumptions)
224
225
        # Generate XML for nodes.
226
        if self.child_nodes:
227
            child_nodes = SubElement(parent, 'Nodes')
228
            for node in self.child_nodes:
229
                node.pre_generate_xml()
230
                node.generate_xml(child_nodes)
231
232
        # Generate XML for output ports.
233
        if self.output_ports:
234
            output_ports = SubElement(parent, 'OutputPorts')
235
            for port in self.output_ports:
236
                port.generate_xml(output_ports)
237
238
    # ------------------------------------------------------------------------------------------------------------------
239
    def get_child_node(self, name):
240
        """
241
        Returns a child node by name. If no child node such name exists an exception is thrown.
242
243
        :param str name: The name of the child node.
244
245
        :rtype: enarksh_lib.xml_generator.node.Node.Node
246
        """
247
        ret = self.search_child_node(name)
248
        if not ret:
249
            raise ValueError("Child node with name '{0}' doesn't exists".format(name))
250
251
        return ret
252
253
    # ------------------------------------------------------------------------------------------------------------------
254
    def get_implicit_dependencies_input_ports(self, port_name, ports, level):
255
        """
256
        :param string port_name:
257
        :param list[] ports:
258
        :param int    level:
259
        """
260
        port = self.get_input_port(port_name)
261
262
        if port not in ports:
263
            if level:
264
                ports.append(port)
265
            port.get_dependencies_ports(ports, level + 1)
266
267
    # ------------------------------------------------------------------------------------------------------------------
268
    def get_implicit_dependencies_output_ports(self, port_name, ports, level):
269
        """
270
        :param string port_name:
271
        :param list[] ports:
272
        :param int    level:
273
        """
274
        port = self.get_output_port(port_name)
275
276
        if port not in ports:
277
            if level:
278
                ports.append(port)
279
            port.get_dependencies_ports(ports, level + 1)
280
281
    # ------------------------------------------------------------------------------------------------------------------
282
    def get_input_port(self, name):
283
        """
284
        Returns input port with 'name'. If no input port with 'name' exists an exception is thrown.
285
286
        :param string name: The name of the port.
287
288
        :rtype: enarksh_lib.xml_generator.port.Port.Port
289
        """
290
        ret = self.search_input_port(name)
291
292
        if not ret:
293
            if name == self.ALL_PORT_NAME:
294
                ret = self.make_input_port(name)
295
            else:
296
                raise Exception("Node '{0}' doesn't have input port '{1}'".format(self.name, name))
297
298
        return ret
299
300
    # ------------------------------------------------------------------------------------------------------------------
301
    def get_output_port(self, name):
302
        """
303
        Returns output port with 'name'. If no output port with 'name' exists, an exception is thrown.
304
305
        :param str name: The name of the output port.
306
307
        :rtype: enarksh_lib.xml_generator.port.Port.Port
308
        """
309
        ret = self.search_output_port(name)
310
311
        if not ret:
312
            if name == self.ALL_PORT_NAME:
313
                ret = self.make_output_port(name)
314
            else:
315
                raise Exception("Node '{0}' doesn't have output port '{1}'".format(self.name, name))
316
317
        return ret
318
319
    # ------------------------------------------------------------------------------------------------------------------
320
    def get_path(self):
321
        """
322
        Returns the path of this node.
323
324
        :rtype: str
325
        """
326
        # -- @todo detect recursion
327
        path = self.parent.get_path() if self.parent else "/"
328
329
        return path + self.name
330
331
    # ------------------------------------------------------------------------------------------------------------------
332
    def make_input_port(self, name):
333
        """
334
        Creates an input port with name 'name' and returns that input port.
335
336
        :param str name: The name of port.
337
338
        :rtype: enarksh_lib.xml_generator.port.Port.Port
339
        """
340
        # -- @todo test port already exists.
341
342
        port = InputPort(self, name)
343
        self.input_ports.append(port)
344
345
        return port
346
347
    # ------------------------------------------------------------------------------------------------------------------
348
    def make_output_port(self, name):
349
        """
350
        Creates an output port with name 'name' and returns that output port.
351
352
        :param str name: The name of port.
353
354
        :rtype: enarksh_lib.xml_generator.port.Port.Port
355
        """
356
        # -- @todo test port already exists.
357
358
        port = OutputPort(self, name)
359
        self.output_ports.append(port)
360
361
        return port
362
363
    # ------------------------------------------------------------------------------------------------------------------
364
    def pre_generate_xml(self):
365
        """
366
        This function can be called before generation XML and is intended to be overloaded.
367
368
        :rtype: None
369
        """
370
        # Nothing to do.
371
        pass
372
373
    # ------------------------------------------------------------------------------------------------------------------
374
    def purge(self):
375
        """
376
        Removes duplicate dependencies and dependencies that are dependencies of predecessors.
377
        """
378
        for port in self.input_ports:
379
            port.purge()
380
381
        for node in self.child_nodes:
382
            node.purge()
383
384
        for port in self.output_ports:
385
            port.purge()
386
387
    # ------------------------------------------------------------------------------------------------------------------
388
    def remove_child_node(self, node_name):
389
        """
390
        Removes node 'node_name' as a child node. The dependencies of any successor of 'node' will be replaced
391
        with all dependencies of the removed node.
392
393
        :param str node_name:
394
        """
395
        node = None
396
397
        # Find and remove node 'node_name'.
398
        for tmp in self.child_nodes:
399
            if tmp.name == node_name:
400
                node = tmp
401
                self.child_nodes.remove(tmp)
402
                break
403
404
        if not node:
405
            raise Exception("Node '{0}' doesn't have child node '{1}'".format(self.get_path(), node_name))
406
407
        # Get all dependencies of the node.
408
        deps = []
409
        for port in node.input_ports:
410
            for dep in port.get_all_dependencies():
411
                deps.append(dep)
412
413
        for tmp in self.child_nodes:
414
            tmp.replace_node_dependency(node_name, deps)
415
416
        for port in self.output_ports:
417
            port.replace_node_dependency(node_name, deps)
418
419
    # ------------------------------------------------------------------------------------------------------------------
420
    def replace_node_dependency(self, node_name, dependencies):
421
        """
422
        Replaces any dependency of this node on node 'node_name' with dependencies 'dependencies'.
423
424
        :param str    node_name:
425
        :param list[] dependencies:
426
        """
427
        for port in self.input_ports:
428
            port.replace_node_dependency(node_name, dependencies)
429
430
    # ------------------------------------------------------------------------------------------------------------------
431
    def search_child_node(self, name):
432
        """
433
        If this node has a child node with name 'name' that child node will be returned.
434
        If no child node with 'name' exists, returns None.
435
436
        :param str name: The name of the child node.
437
438
        :rtype: None|enarksh_lib.xml_generator.node.Node.Node
439
        """
440
        ret = None
441
        for node in self.child_nodes:
442
            if node.name == name:
443
                ret = node
444
                break
445
446
        return ret
447
448
    # ------------------------------------------------------------------------------------------------------------------
449
    def search_input_port(self, name):
450
        """
451
        If this node has a input port with name 'name' that input port will be returned.
452
        If no input port with 'name' exists, returns None.
453
454
        :param str name: The name of the input port.
455
456
        :rtype: None|enarksh_lib.xml_generator.port.InputPort.InputPort
457
        """
458
        ret = None
459
        for port in self.input_ports:
460
            if port.port_name == name:
461
                ret = port
462
                break
463
464
        return ret
465
466
    # ------------------------------------------------------------------------------------------------------------------
467
    def search_output_port(self, name):
468
        """
469
        If this node has a output port with name 'name' that output port will be returned.
470
        If no output port with 'name' exists, returns None.
471
472
        :param str name: The name of the output port.
473
474
        :rtype: None|enarksh_lib.xml_generator.port.InputPort.InputPort
475
        """
476
        ret = None
477
        for port in self.output_ports:
478
            if port.port_name == name:
479
                ret = port
480
                break
481
482
        return ret
483
484
    # ------------------------------------------------------------------------------------------------------------------
485
    def ensure_dependencies(self):
486
        """
487
        Creates the following dependencies:
488
        - Dependencies between the input port 'all' and the input port 'all' of all the child nodes of this nodes.
489
        - Dependencies between all output ports 'all' of all child nodes and the output port 'all' of this node.
490
        - Dependencies between the input port 'all' of this node and the output ports 'all' of all predecessor nodes of
491
          this node.
492
        This is done recursively for all child node.
493
494
        Remember: Redundant and duplicate dependencies are removed by purge().
495
        """
496
        if self.child_nodes:
497
            # Apply this method recursively for all child node.
498
            for node in self.child_nodes:
499
                node.ensure_dependencies()
500
501
            # Ensure that the input port 'all' of all child nodes depends on input port 'all' of this node.
502
            self.add_dependency_all_input_ports()
503
504
            # Ensure that output port 'all' of the node depends on all output ports 'all' of all child nodes.
505
            self.add_dependency_all_output_ports()
506
507
            # Ensure input port 'all' of this node depends on output 'all' of each predecessor of this node.
508
            input_port_all = self.get_input_port(self.ALL_PORT_NAME)
509
            for input_port in self.input_ports:
510
                for port in input_port.get_all_dependencies():
511
                    if port.node != self.parent:
512
                        input_port_all.add_dependency(port.node.get_output_port(self.ALL_PORT_NAME))
513
514
# ----------------------------------------------------------------------------------------------------------------------
515