1
+ import click
2
+ from getpass import getpass
3
+ import os
4
+ import sys
5
+
6
+ from swsscommon .swsscommon import SonicV2Connector
7
+
8
+ CHASSIS_MODULE_INFO_TABLE = 'CHASSIS_MODULE_TABLE'
9
+ CHASSIS_MODULE_INFO_KEY_TEMPLATE = 'CHASSIS_MODULE {}'
10
+ CHASSIS_MODULE_INFO_DESC_FIELD = 'desc'
11
+ CHASSIS_MODULE_INFO_SLOT_FIELD = 'slot'
12
+ CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status'
13
+ CHASSIS_MODULE_INFO_ADMINSTATUS_FIELD = 'admin_status'
14
+
15
+ CHASSIS_MIDPLANE_INFO_TABLE = 'CHASSIS_MIDPLANE_TABLE'
16
+ CHASSIS_MIDPLANE_INFO_IP_FIELD = 'ip_address'
17
+ CHASSIS_MIDPLANE_INFO_ACCESS_FIELD = 'access'
18
+
19
+ CHASSIS_MODULE_HOSTNAME_TABLE = 'CHASSIS_MODULE_HOSTNAME_TABLE'
20
+ CHASSIS_MODULE_HOSTNAME = 'module_hostname'
21
+
22
+ def connect_to_chassis_state_db ():
23
+ chassis_state_db = SonicV2Connector (host = "127.0.0.1" )
24
+ chassis_state_db .connect (chassis_state_db .CHASSIS_STATE_DB )
25
+ return chassis_state_db
26
+
27
+
28
+ def connect_state_db ():
29
+ state_db = SonicV2Connector (host = "127.0.0.1" )
30
+ state_db .connect (state_db .STATE_DB )
31
+ return state_db
32
+
33
+
34
+
35
+ def get_linecard_module_name_from_hostname (linecard_name : str ):
36
+
37
+ chassis_state_db = connect_to_chassis_state_db ()
38
+
39
+ keys = chassis_state_db .keys (chassis_state_db .CHASSIS_STATE_DB , '{}|{}' .format (CHASSIS_MODULE_HOSTNAME_TABLE , '*' ))
40
+ for key in keys :
41
+ module_name = key .split ('|' )[1 ]
42
+ hostname = chassis_state_db .get (chassis_state_db .CHASSIS_STATE_DB , key , CHASSIS_MODULE_HOSTNAME )
43
+ if hostname .replace ('-' , '' ).lower () == linecard_name .replace ('-' , '' ).lower ():
44
+ return module_name
45
+
46
+ return None
47
+
48
+ def get_linecard_ip (linecard_name : str ):
49
+ """
50
+ Given a linecard name, lookup its IP address in the midplane table
51
+
52
+ :param linecard_name: The name of the linecard you want to connect to
53
+ :type linecard_name: str
54
+ :return: IP address of the linecard
55
+ """
56
+ # Adapted from `show chassis modules midplane-status` command logic:
57
+ # https://github.com/sonic-net/sonic-utilities/blob/master/show/chassis_modules.py
58
+
59
+ # if the user passes linecard hostname, then try to get the module name for that linecard
60
+ module_name = get_linecard_module_name_from_hostname (linecard_name )
61
+ # if the module name cannot be found from host, assume the user has passed module name
62
+ if module_name is None :
63
+ module_name = linecard_name
64
+ module_ip , module_access = get_module_ip_and_access_from_state_db (module_name )
65
+
66
+ if not module_ip :
67
+ click .echo ('Linecard {} not found' .format (linecard_name ))
68
+ return None
69
+
70
+ if module_access != 'True' :
71
+ click .echo ('Linecard {} not accessible' .format (linecard_name ))
72
+ return None
73
+
74
+
75
+ return module_ip
76
+
77
+ def get_module_ip_and_access_from_state_db (module_name ):
78
+ state_db = connect_state_db ()
79
+ data_dict = state_db .get_all (
80
+ state_db .STATE_DB , '{}|{}' .format (CHASSIS_MIDPLANE_INFO_TABLE ,module_name ))
81
+ if data_dict is None :
82
+ return None , None
83
+
84
+ linecard_ip = data_dict .get (CHASSIS_MIDPLANE_INFO_IP_FIELD , None )
85
+ access = data_dict .get (CHASSIS_MIDPLANE_INFO_ACCESS_FIELD , None )
86
+
87
+ return linecard_ip , access
88
+
89
+
90
+ def get_all_linecards (ctx , args , incomplete ) -> list :
91
+ """
92
+ Return a list of all accessible linecard names. This function is used to
93
+ autocomplete linecard names in the CLI.
94
+
95
+ :param ctx: The Click context object that is passed to the command function
96
+ :param args: The arguments passed to the Click command
97
+ :param incomplete: The string that the user has typed so far
98
+ :return: A list of all accessible linecard names.
99
+ """
100
+ # Adapted from `show chassis modules midplane-status` command logic:
101
+ # https://github.com/sonic-net/sonic-utilities/blob/master/show/chassis_modules.py
102
+
103
+
104
+ chassis_state_db = connect_to_chassis_state_db ()
105
+ state_db = connect_state_db ()
106
+
107
+ linecards = []
108
+ keys = state_db .keys (state_db .STATE_DB ,'{}|*' .format (CHASSIS_MIDPLANE_INFO_TABLE ))
109
+ for key in keys :
110
+ key_list = key .split ('|' )
111
+ if len (key_list ) != 2 : # error data in DB, log it and ignore
112
+ click .echo ('Warn: Invalid Key {} in {} table' .format (key , CHASSIS_MIDPLANE_INFO_TABLE ))
113
+ continue
114
+ module_name = key_list [1 ]
115
+ linecard_ip , access = get_module_ip_and_access_from_state_db (module_name )
116
+ if linecard_ip is None :
117
+ continue
118
+
119
+ if access != "True" :
120
+ continue
121
+
122
+ # get the hostname for this module
123
+ hostname = chassis_state_db .get (chassis_state_db .CHASSIS_STATE_DB , '{}|{}' .format (CHASSIS_MODULE_HOSTNAME_TABLE , module_name ), CHASSIS_MODULE_HOSTNAME )
124
+ if hostname :
125
+ linecards .append (hostname )
126
+ else :
127
+ linecards .append (module_name )
128
+
129
+ # Return a list of all matched linecards
130
+ return [lc for lc in linecards if incomplete in lc ]
131
+
132
+
133
+ def get_password (username = None ):
134
+ """
135
+ Prompts the user for a password, and returns the password
136
+
137
+ :param username: The username that we want to get the password for
138
+ :type username: str
139
+ :return: The password for the username.
140
+ """
141
+
142
+ if username is None :
143
+ username = os .getlogin ()
144
+
145
+ return getpass (
146
+ "Password for username '{}': " .format (username ),
147
+ # Pass in click stdout stream - this is similar to using click.echo
148
+ stream = click .get_text_stream ('stdout' )
149
+ )
0 commit comments