-
Notifications
You must be signed in to change notification settings - Fork 0
DO NOT MERGE #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
DO NOT MERGE #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
# Python 3.10 has some nice features, but I only have 3.8 installed. | ||
# Importing __future__ lets us use the features that won't break anything. | ||
# It isn't as good as updating and getting ALL the features, but it's nice to have some. | ||
from __future__ import annotations | ||
|
||
import csv | ||
from getpass import getpass | ||
from typing import Any, Dict, List, Tuple | ||
|
||
from netmiko import BaseConnection, ConnectHandler, ReadTimeout | ||
|
||
DEFAULT_DEVICE_TYPE: str = 'cisco_ios' | ||
DEFAULT_WHID: str = "YEG1" | ||
IF_DETAIL_CMD: str = "sh idprom interface {iface} detail" | ||
HOSTS_FILE = "/home/segattod/segattod-workspace/src/Segattod-package/myproject/Light_level_checks/list.txt" | ||
MIN_LIGHT_LEVEL: float = -10 | ||
MODEL_LINE_PREFIX: str = "Vendor Name" | ||
SPEED_LINE_PREFIX: str = "Administrative Speed" | ||
|
||
SFP_SPEEDS: Dict[int, str] = {10000: "10G", 1000: "1G"} | ||
|
||
|
||
def _login() -> Tuple[str, str]: | ||
# A tuple lets us return two different values at once! Making this a method means | ||
# it is easier to change later on if we ever decide to change how we log in. Maybe | ||
# we can use SSO or something one day? That would be cool. | ||
return input('User Name:'), getpass() | ||
|
||
|
||
def _get_whid() -> str: | ||
# You said you wanted to replace that hardcoded string with a user input or something | ||
# later, now you can just change this here and not mess with your `main` code. This | ||
# is called the Interface Design Principle. You can write a dozen different ways to get | ||
# a WHID and add code here that says "if on the VPN then use SSO; if there is no keyboard | ||
# then default to "YEG1" otherwise ask the user for input and each of those options can | ||
# be in their own methods... Once you have this interface class, main doesn't care HOW | ||
# you get the WHID, as long as this method returns a string. | ||
return DEFAULT_WHID | ||
|
||
|
||
def _create_connection( | ||
_host: str, | ||
_username: str, | ||
_password: str, | ||
# If the user doesn't provide a secret then we will default to using the password below. | ||
_secret: str | None = None, | ||
# If the user doesn't provide a device type then use the default. | ||
_device_type: str = DEFAULT_DEVICE_TYPE, | ||
) -> BaseConnection | None: | ||
try: | ||
_connection: BaseConnection = ConnectHandler( | ||
host=_host, | ||
device_type=_device_type, | ||
username=_username, | ||
password=_password, | ||
secret=_secret or _password # If _secret is None then use _password here instead | ||
) | ||
_connection.send_command('terminal length 0') | ||
_connection.enable() | ||
return _connection | ||
|
||
except ReadTimeout as raised: | ||
print(f'Failed connection {host}: {raised}') | ||
return None | ||
except ValueError as raised: | ||
print(f'ValueError raised connecting to {host}: {raised}: ') | ||
return None | ||
|
||
|
||
def _parse_response_data(conn: BaseConnection, iface: str) -> Tuple[str, str]: | ||
"""I have no way of testing if this will actually work, it's just here as an example.""" | ||
response: str = conn.send_command(IF_DETAIL_CMD.format(iface=iface)) | ||
|
||
# Initialize these to blank strings to stat with. | ||
# Python treats a blank string as False in an if statement. | ||
model: str = "" | ||
speed: str = "" | ||
# For each line in the response: | ||
for line in response.splitlines(): | ||
# Split at the colon, save the right side, and strip any extra blank spaces. | ||
line_data: str = line.split(":")[1].rstrip() | ||
# If the left side startswith something we want, save it. | ||
if line.startswith(MODEL_LINE_PREFIX): | ||
model = line_data | ||
elif line.startswith(SPEED_LINE_PREFIX): | ||
speed = line_data | ||
|
||
# If we have both values then return them. There is no reason to look at the rest of the lines. | ||
if model and speed: | ||
return model, speed | ||
|
||
# If we finished looking at every line and have not found both values, we have a problem. | ||
raise ValueError( | ||
f"Provided data did not include model and speed information in the expected format. {response}" | ||
) | ||
|
||
|
||
def _get_response_data(conn: BaseConnection, iface: str) -> Tuple[str, str]: | ||
""" | ||
TECHNICALLY I think it is best practice to call just the base command and | ||
pull out the data you need from the full response. That is preferred because | ||
this is making two network calls instead of one. In our case it's trivial, | ||
but if you were connecting to a service that charged per connection or had | ||
a max number of connections per hour or something, getting it in one call | ||
then parsing it would save time and heartache. It's a good habit to be in. | ||
|
||
See _parse_response_data() above for a suggestion. | ||
""" | ||
|
||
# Take the command from up above and replace the {iface} placeholder: | ||
base_cmd: str = IF_DETAIL_CMD.format(iface=iface) | ||
# Then use that to build your two commands. | ||
model_data: str = conn.send_command(f'{base_cmd} | i Vendor Name') | ||
speed_data: str = conn.send_command(f'{base_cmd} | i Administrative Speed') | ||
|
||
return model_data, speed_data | ||
|
||
|
||
def _light_level_check(conn: BaseConnection) -> None: | ||
transceiver_output: Dict[str, Any] = conn.send_command( | ||
'show interface transceiver', | ||
read_timeout=90, | ||
# I can't actually run this script, so you may need to tinker a little. | ||
# I suspect you can drop the following line | ||
use_textfsm=True | ||
) | ||
|
||
for transceiver in transceiver_output: | ||
interface: str = transceiver['iface'] | ||
model_data, speed_data = _get_response_data(connection, interface) | ||
model: str = 'Cisco' if 'CISCO' in model_data.upper() else 'Non-Cisco' | ||
try: | ||
speed_as_str: str = speed_data.split(":")[1] | ||
speed = SFP_SPEEDS[int(speed_as_str)] | ||
except (ValueError, IndexError): | ||
# Return "Unknown" if we get an integer we don't recognize or a non-integer value. | ||
speed = "Unknown" | ||
|
||
pwr: str = transceiver['rx_pwr'] | ||
try: | ||
state = 'Pass' if float(pwr) >= MIN_LIGHT_LEVEL else 'Fail' | ||
except ValueError: # Could not convert to a float | ||
state = 'No Connection' | ||
|
||
formatted_data.append((host, interface, pwr, state, model, speed)) | ||
|
||
|
||
if __name__ == '__main__': | ||
username, password = _login() | ||
formatted_data: List = [] | ||
whid: str = _get_whid() | ||
output_filename = f"{whid}_output_data.csv" | ||
|
||
print('Beginning Script') | ||
with open(HOSTS_FILE, 'r') as file: | ||
for host in file.readlines(): | ||
print(f"Connecting to {host}") | ||
connection = _create_connection(host, username, password) | ||
_light_level_check(connection) | ||
|
||
with open(output_filename, "w", newline="") as csvfile: | ||
csv_writer = csv.writer(csvfile) | ||
# Write CSV header. | ||
csv_writer.writerow(["Host", "interface", "rx_pwr", "result", "SFP Model", "SFP Speed"]) | ||
# Write the data. | ||
csv_writer.writerows(formatted_data) | ||
|
||
print(f"Data exported to {output_filename}") |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,92 @@ | ||||||||||||||||||||||||||||||
import getpass | ||||||||||||||||||||||||||||||
import netmiko | ||||||||||||||||||||||||||||||
# json was here bc as I'm using textfsm to pull data from cisco devices | ||||||||||||||||||||||||||||||
# it comes in json i needed to show the output before deciding how to tread the date | ||||||||||||||||||||||||||||||
import json | ||||||||||||||||||||||||||||||
import csv | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Where i'm pulling the devices list / Soon I'll make this automated which will pull direct from NARF | ||||||||||||||||||||||||||||||
file = open(r'/home/segattod/segattod-workspace/src/Segattod-package/myproject/Light_level_checks/list.txt', | ||||||||||||||||||||||||||||||
'r') | ||||||||||||||||||||||||||||||
file = file.read().splitlines() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
username = input('User Name:') | ||||||||||||||||||||||||||||||
password = getpass.getpass() | ||||||||||||||||||||||||||||||
Comment on lines
+8
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a general rule, these should be inside a method. There isn't really any reason to have commands outside of a method. Save the top up here for declaring constants, and everything else gets wrapped up tidy. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Variable for "append" the data later | ||||||||||||||||||||||||||||||
formatted_data = [] | ||||||||||||||||||||||||||||||
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would get moved into "main" when you implement it. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# for now just to name the file / Soon to add for user to add input :) | ||||||||||||||||||||||||||||||
whid = 'YEG1' | ||||||||||||||||||||||||||||||
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By convention, names of constants are always in all caps
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay, even if later I'll make it to ask user the "whid "input, I should make it all capital to mean it should not changed, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm actually talking about that in the other file I'm going to upload. it's an interesting one. In general, the value should never change so I'd say yes, all caps. BUT from the other point of view, it IS changing once because it is being set. In the end, it could go either way, I guess. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Function to sorte the data | ||||||||||||||||||||||||||||||
def light_level_check(connection): | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By convention, starting a method/function name with an underscore shows others that it's not meant for them to use. If someone builds on your package, would you expect them to want to use this function? If so, then this should return as you mentioned below. if not, then start it with an underscore and it can do whatever it wants because it's "private". In this case, it's up to you. but defaulting to an underscore is never a bad idea. |
||||||||||||||||||||||||||||||
# Command to checks light level per interface | ||||||||||||||||||||||||||||||
transceiver_output = connection.send_command('show interface transceiver', read_timeout=90, | ||||||||||||||||||||||||||||||
use_textfsm='~/ntc-templates/ntc_templates/templates/cisco_ios_show_interface_transceiver.textfsm') | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the code for You didn't get any errors here because Python is a "Truthy" language. When it expects a boolean and it gets pretty much anything, it assumes that is True. It's smart enough to know that an empty string is False, and the integer 0 is false, but the string "banana" or any number other than 0 is treated as a True. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if i get your question right, but zthe output from this command will be formatted as the textfsm template, which should look just like json. here is the output how it looks like (ps: i added just 2 lines/interfaces output but it could have many)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html#netmiko.base_connection.BaseConnection.send_command In their documentation, they say that
Suggested change
Comment on lines
+26
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python is super flexible on whitespace. the suggestion below is the same as far as python cares, but way easier for a human to read:
Suggested change
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
for x in transceiver_output: | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea what x is here, or what |
||||||||||||||||||||||||||||||
# added both variables below to do a check at that moment based on the "output" for that device interface the loop is going through | ||||||||||||||||||||||||||||||
# why? bc was the way i could think to append(look below) all data to export later in CSV | ||||||||||||||||||||||||||||||
# basically i use the "entry" information from "x" do all the checks and move to the next, and so on. | ||||||||||||||||||||||||||||||
sfpmodel = connection.send_command(f'sh idprom interface {x["iface"]} detail | i Vendor Name') | ||||||||||||||||||||||||||||||
sfpspeed = connection.send_command( | ||||||||||||||||||||||||||||||
f'sh idprom interface {x["iface"]} detail | i Administrative Speed') | ||||||||||||||||||||||||||||||
Comment on lines
+33
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a design Principle called DRY: "Don't Repeat Yourself". If you re typing the same thing more than once, it's likely faster and easier to use a variable. Try this
Suggested change
Similarly, sftmodel and sftspeed are not very "pythonic" variable names. At a miniumumm, I'd suggest Don't be afraid of long names, they make life easier when you come back to this in a month and forget what everything meant. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Checking if that entry maches with 'cisco' | ||||||||||||||||||||||||||||||
if 'CISCO' in sfpmodel.upper(): | ||||||||||||||||||||||||||||||
sfpM = 'Cisco' | ||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||
sfpM = 'Non-Cisco' | ||||||||||||||||||||||||||||||
Comment on lines
+37
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When there are only two possible outcomes like this, you can use a Ternary Operator:
Suggested change
|
||||||||||||||||||||||||||||||
# Checking if that entry has a match with 10000, then with 1000 | ||||||||||||||||||||||||||||||
if '10000' in sfpspeed: | ||||||||||||||||||||||||||||||
sfpS = '10GB' | ||||||||||||||||||||||||||||||
elif '1000' in sfpspeed: | ||||||||||||||||||||||||||||||
sfpS = '1GB' | ||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||
sfpS = 'Unknown' | ||||||||||||||||||||||||||||||
Comment on lines
+42
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an interesting one. The way you did it is quite clever, and nothing wrong with that. I may try to come up with another way. |
||||||||||||||||||||||||||||||
# checking if the light level for fiber connections are acceptable | ||||||||||||||||||||||||||||||
# AND add all that compiled data to "formatted_data" | ||||||||||||||||||||||||||||||
## I know I could have used "return" but when i did it it was not "appending" | ||||||||||||||||||||||||||||||
## it was adding all for one device and erasing and add the new ones, should be simple mistake from my end but i could not figure lol | ||||||||||||||||||||||||||||||
Comment on lines
+51
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah, using append is perfectly fine here. I suspect the reason it was overwriting is because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't roll back to what i did to confirm, but i do believe it did exactly what you suggested,but if you think the way it is, looks good and not like a "remediate way to make it work" then should be fine ;) |
||||||||||||||||||||||||||||||
if x['rx_pwr'] == 'N/A': | ||||||||||||||||||||||||||||||
formatted_data.append((host, x['iface'], x["rx_pwr"], 'No Connection', sfpM, sfpS)) | ||||||||||||||||||||||||||||||
elif float(x['rx_pwr']) >= -10: | ||||||||||||||||||||||||||||||
formatted_data.append((host, x['iface'], x["rx_pwr"], 'Pass', sfpM, sfpS)) | ||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||
formatted_data.append((host, x['iface'], x["rx_pwr"], 'Fail', sfpM, sfpS)) | ||||||||||||||||||||||||||||||
Comment on lines
+53
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stay DRY:
BUT that can be simplified even more using try/except as control logic:
Suggested change
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# where script "starts", i could have done a __MAIN__ here but didn't get much how it works yet, currently learning this ;P | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it should :P |
||||||||||||||||||||||||||||||
print('Beginning Script') | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Loop to go over device list linked on the beginning | ||||||||||||||||||||||||||||||
## I know I can add the connection to a funcion as well, i have it on another script just didn't get to do it as this looks cleaner for me(but i know should be updated) | ||||||||||||||||||||||||||||||
for host in file: | ||||||||||||||||||||||||||||||
print(f'Connecting to {host}') | ||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||
connection = netmiko.ConnectHandler(host=host, device_type='cisco_ios', username=username, | ||||||||||||||||||||||||||||||
password=password, secret=password) | ||||||||||||||||||||||||||||||
Comment on lines
+69
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this one i was lazy, i did this way on the test but didn't change here when first writing script/code for me straight line felt easier There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All in one line is fine if it fits and you like it. My team uses 120 character as the maximum line length, as a guide for "fits on one line" |
||||||||||||||||||||||||||||||
connection.send_command('terminal length 0') | ||||||||||||||||||||||||||||||
connection.enable() | ||||||||||||||||||||||||||||||
except Exception as e: | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's considered poor form to catch Exception, you should always be as explicit as possible here so you don't accidentally swallow an error you didn't mean to. It looks like ConnectHandler and enable could raise ValueError, send_command can raise ReadTimeout. You could replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my idea was to have the device and error shown, which i could later come back and check the error from the output and try is later. but yeah its a good idea to have it retry depending on the error! noted! |
||||||||||||||||||||||||||||||
print(f'Failed connection {host}: {e}') | ||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||
# call the funcion (and only one lol) | ||||||||||||||||||||||||||||||
light_level_check(connection) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Define CSV file name + WHID | ||||||||||||||||||||||||||||||
csv_filename = (f"{whid}_output_data.csv") | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Export formatted data to CSV | ||||||||||||||||||||||||||||||
with open(csv_filename, "w", newline="") as csvfile: | ||||||||||||||||||||||||||||||
csv_writer = csv.writer(csvfile) | ||||||||||||||||||||||||||||||
csv_writer.writerow( | ||||||||||||||||||||||||||||||
["Host", "interface", "rx_pwr", "result", "SFP Model", "SFP Speed"]) # Write CSV header | ||||||||||||||||||||||||||||||
csv_writer.writerows(formatted_data) # Write the data | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
print(f"Data exported to {csv_filename}") | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
## I know i could make a main function and invoke others but im still going through it in my learning path lol | ||||||||||||||||||||||||||||||
## Also should use a more structured start for the script using main or other way. | ||||||||||||||||||||||||||||||
Comment on lines
+91
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both of these are true statements :P Also, by convention, every python script/file should end in a blank line. The reason for it is "old technology", it's a throwback to when most people used a terminal. when you Which brings me to Linters. You should install one. A linter is a script that will go through your code and clean it up. Depending on your settings it can do thins like make sure you always use the same quotes (python doesn't care if you use 'single' or "double" quotes, but it's generally expected that you will pick one and stick with it) and it can make sure you have the right number of blank lines before a method or class, etc. most of those things they can even fix for you magically, so you just run the command and it makes your code pretty. my team uses |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from result import result | ||
|
||
MODEL_LINE_PREFIX: str = "Vendor Name" | ||
SPEED_LINE_PREFIX: str = "Administrative Speed" | ||
|
||
""" | ||
These two lines have the same result as the _parse_response_data() method in day1_rewrite.py. | ||
Instead of getting the data from send_command(), I'm importing it from result.py so you can | ||
actually run it and see that it's not just gibberish. :P The `data = ` line is what is called a | ||
"dictionary comprehension". Super powerful, and often super confusing. Don't use this kind of | ||
thing when someone else may need to read your code, but once you learn them they are really fun | ||
to play with. | ||
|
||
Line 29 does all the following in one command: | ||
|
||
create a new dictionary named data | ||
for each line in result: | ||
if the line starts with MODEL_LINE_PREFIX or SPEED_LINE_PREFIX: | ||
add an entry to the dictionary with the matching PREFIX as key and the rest of the line as the value | ||
|
||
|
||
Then line 30 pulls those two values out of the dictionary and returns them as a tuple. | ||
|
||
Of course, when it takes more lines to explain it than to do it, you are likely something it wrong. | ||
""" | ||
|
||
|
||
def _parse(): | ||
data = {line.split(":")[0].strip() : line.split(":")[1].strip() for line in result.splitlines() if line.startswith((MODEL_LINE_PREFIX, SPEED_LINE_PREFIX))} | ||
return data[MODEL_LINE_PREFIX], data[SPEED_LINE_PREFIX] | ||
|
||
|
||
model, speed = _parse() | ||
print(f'Model: {model}') | ||
print(f'Speed: {speed}') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
result = """ | ||
General SFP Information | ||
----------------------------------------------- | ||
Identifier : SFP/SFP+ | ||
Ext.Identifier : SFP function is defined by two-wire interface ID only | ||
Connector : LC connector | ||
Transceiver | ||
10GE Comp code : Unknown | ||
SONET Comp code : OC 48 short reach | ||
GE Comp code : Unknown | ||
Link length : Unknown | ||
Technology : Unknown | ||
Media : Single Mode | ||
Speed : Unknown | ||
Encoding : 64B/66B | ||
BR_Nominal : 10300 Mbps | ||
Length(9um)-km : 10 km | ||
Length(9um) : 10000 m | ||
Length(50um) : GBIC does not support 50 micron multi mode OM2 fibre | ||
Length(62.5um) : GBIC does not support 62.5 micron multi mode OM1 fibre | ||
Length(Copper) : GBIC does not support 50 micron multi mode OM4 fibre | ||
Vendor Name : CISCO-FINISAR | ||
Vendor Part Number : FTLX1475D3BCL-C2 | ||
Vendor Revision : 0x41 0x20 0x20 0x20 | ||
Vendor Serial Number : FNS24150LJV | ||
Wavelength : 1310 nm | ||
CC_BASE : 0xC5 | ||
----------------------------------------------- | ||
|
||
Extended ID Fields | ||
----------------------------------------------- | ||
Options : 0x00 0x1A | ||
BR, max : 0x00 | ||
BR, min : 0x00 | ||
Date code : 200409 | ||
Diag monitoring : Implemented | ||
Internally calibrated : Yes | ||
Exeternally calibrated: No | ||
Rx.Power measurement : Avg.Power | ||
Address Change : Not Required | ||
CC_EXT : 0x56 | ||
----------------------------------------------- | ||
|
||
Other Information | ||
----------------------------------------------- | ||
Chk for link status : 00 | ||
Flow control Receive : Off | ||
Flow control Send : Off | ||
Administrative Speed : 10000 | ||
Administrative Duplex : full | ||
Operational Speed : 10000 | ||
Operational Duplex : full | ||
----------------------------------------------- | ||
|
||
SEEPROM contents (hex): | ||
0x00: 03 04 07 20 00 00 00 00 00 00 00 06 67 00 0A 64 | ||
0x10: 00 00 00 00 43 49 53 43 4F 2D 46 49 4E 49 53 41 | ||
0x20: 52 20 20 20 00 00 90 65 46 54 4C 58 31 34 37 35 | ||
0x30: 44 33 42 43 4C 2D 43 32 41 20 20 20 05 1E 00 C5 | ||
0x40: 00 1A 00 00 46 4E 53 32 34 31 35 30 4C 4A 56 20 | ||
0x50: 20 20 20 20 32 30 30 34 30 39 20 20 68 F0 06 56 | ||
0x60: 00 00 02 C2 4C 1D 0E 2B 09 6C 77 24 44 8C BD 46 | ||
0x70: F5 F7 3F 00 00 00 00 00 00 00 00 00 94 1C CE 86 | ||
----------------------------------------------- | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is generally a bad idea. below you used the
with
context manager like this:And that is the "right" way. One of the main problems with this way here is that you never closed the file. If someone else tried to open that file even if you aren't currently running your script, they could run into errors because it's locked by you. The operating system should eventually realize your script is done and eventually unlock it, but it's a bad idea to cross your fingers and hope.
The context manager magically closes the file when the code leaves the manager (hits the unindent) so that is never an issue.
Which generally also means it should be inside a function or method. But you already know that :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually didn't know it was a best practice just felt right to move it to a variable and work with that after(LOL)
You mentioned "the context manager magically closes .." do you mean the way i did it, is good, it will always close automatically after "moving" the content to the variable, right?
just confirming if it go it right lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You used a context manager the second time, down when you saved the data.
This is a context manager:
After do_stuff(), the manager will close the file before moving on to do_more_stuff().