def get_testbed(lab, host, user, passwd): client_library = ClientLibrary("https://" + host, user, passwd, ssl_verify=False) lab = client_library.join_existing_lab(args.lab) return lab.get_pyats_testbed()
def test_connect(client_library: ClientLibrary): lab = client_library.create_lab("my lab name") lab = client_library.join_existing_lab(lab.id) lab.auto_sync = False s1 = lab.create_node("s1", "server", 50, 100) s2 = lab.create_node("s2", "server", 50, 200) print(s1, s2) # create a link between s1 and s2 s1_i1 = s1.create_interface() s2_i1 = s2.create_interface() lab.create_link(s1_i1, s2_i1) # this must remove the link between s1 and s2 lab.remove_node(s2) lab.sync_states() for node in lab.nodes(): print(node, node.state) for iface in node.interfaces(): print(iface, iface.state) assert [link for link in lab.links() if link.state is not None] == []
def stop_wipe_and_remove_all_labs(client_library: ClientLibrary): lab_list = client_library.get_lab_list() for lab_id in lab_list: lab = client_library.join_existing_lab(lab_id) lab.stop() lab.wipe() client_library.remove_lab(lab_id)
def test_sync_lab(register_licensing, client_library: ClientLibrary): lab = client_library.create_lab("my test lab name") lab = client_library.join_existing_lab(lab.id) r1 = lab.create_node("r1", "server", 5, 100) r2 = lab.create_node("r2", "server", 102, 201) r3 = lab.create_node("r3", "server", 200, 400) # print(r1, r2, r3) r1.x = 400 r1.label = "abc" r1_i1 = r1.create_interface() r1_i2 = r1.create_interface() r2_i1 = r2.create_interface() r2_i2 = r2.create_interface() r3_i1 = r3.create_interface() r3_i2 = r3.create_interface() # lab.create_link(r1_i1, r2_i1) lab.create_link(r2_i2, r3_i1) lab.create_link(r3_i2, r1_i2) r1.start() r2.start() r3.start() # lab.stop() r1.stop() r2.stop() r3.stop() # lab.remove_link(link_1) # lab.remove_link(link_2) # lab.remove_link(link_3) # lab.remove_node(r1) # lab.remove_node(r2) # lab.remove_node(r3) # TODO: wait for convergence here lab.stop()
def lab_download(self): """ Imports an existing topology from a CML2 server. Downloaded configuration is stored in self.lab_conf class variable and parsed as YAML object :raises TypeError: if no lab_id is provided :raises requests.exceptions.HTTPError: if there was a transport error """ cl = ClientLibrary(url="https://" + self._cmlnetkitconfig.host, username=self._cmlnetkitconfig.username, password=self._cmlnetkitconfig.password, ssl_verify=self._cmlnetkitconfig.ssl_verify) cl.wait_for_lld_connected() try: self.lab_handler = cl.join_existing_lab( self._cmlnetkitconfig.lab_id) self.lab_conf = yaml.safe_load(self.lab_handler.download()) except TypeError: print( "TypeError: No lab_id provided. Use the -l option to provide the lab_id" )
LAB_NAME = input("enter lab name: ") client = ClientLibrary(VIRL_CONTROLLER, VIRL_USERNAME, VIRL_PASSWORD, ssl_verify=False) # Find the lab by title and join it as long as it's the only # lab with that title. labs = client.find_labs_by_title(LAB_NAME) if not labs or len(labs) != 1: print("ERROR: Unable to find a unique lab named {}".format(LAB_NAME)) exit(1) lobj = client.join_existing_lab(labs[0].id) if not lobj: print("ERROR: Failed to join lab {}".format(LAB_NAME)) exit(1) # Print all links in the lab and ask which link to condition. i = 1 liobjs = [] for link in lobj.links(): print("{}. {}[{}] <-> {}[{}]".format( i, link.interface_a.node.label, link.interface_a.label, link.interface_b.node.label, link.interface_b.label,
# setup the connection and clean everything cl = ClientLibrary("http://localhost:8001", "cml2", "cml2cml2", allow_http=True) cl.is_system_ready(wait=True) # set transport if needed - also proxy can be set if needed # cl.licensing.licensing.set_transport(ssms=ssms, proxy_server="172.16.1.100", proxy_port=8888) cl.licensing.set_transport(ssms=SSMS) cl.licensing.install_certificate(cert=CERT) # 'register_wait' method waits max 45s for registration status to become COMPLETED # and another 45s for authorization status to become IN_COMPLIANCE cl.licensing.register_wait(token=TOKEN) lab_list = cl.get_lab_list() for lab_id in lab_list: lab = cl.join_existing_lab(lab_id) lab.stop() lab.wipe() cl.remove_lab(lab_id) lab = cl.create_lab() lab = cl.join_existing_lab(lab_id="lab_1") s1 = lab.create_node("s1", "server", 50, 100) s2 = lab.create_node("s2", "server", 50, 200) print(s1, s2) # create a link between s1 and s2, equivalent to # s1_i1 = s1.create_interface() # s2_i1 = s2.create_interface() # lab.create_link(s1_i1, s2_i1)
class CML(object): def __init__(self, host, username, password): logger = logging.getLogger("virl2_client.virl2_client") level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) # Remove VIRL2 envvars if they exist. These would conflict with the virlutils config. os.environ.pop("VIRL2_USER", None) os.environ.pop("VIRL2_PASS", None) os.environ.pop("VIRL2_URL", None) self._host = host self._username = username self._password = password self._consoles = {} self._student = None self._student_password = None self._student_name = None self._client = ClientLibrary(host, username, password, raise_for_auth_failure=True, ssl_verify=False) logger.setLevel(level) def import_lab(self, filename, title): lab = self._client.import_lab_from_path(filename, title=title) return lab.id def configure_lab(self, lid, student, name, passwd, cfg_dir): self._student = student self._student_password = passwd self._student_name = name lab = self._client.join_existing_lab(lid) for node in lab.nodes(): if node.label == "jump-host": continue nconfig = cfg_dir + "/" + node.label + ".cfg" if os.path.isfile(nconfig): with open(nconfig, "r") as fd: node.config = fd.read() def _configure_breakout(self, lab): jump_host = lab.get_node_by_label("jump-host") config = f"""\ #cloud-config password: cisco chpasswd: {{ expire: False }} hostname: jump-host ssh_pwauth: True users: - default - name: {self._student} gecos: {self._student_name} plain_text_passwd: '{self._student_password}' lock_passwd: false password: '******' shell: /bin/bash write_files: - content: | console_start_port: {CONSOLE_BASE_PORT} controller: https://{self._host} username: {self._student} password: '******' populate_all: false verify_tls: false listen_address: '0.0.0.0' path: /etc/breakout/config.yaml - content: | {lab.id}: enabled: true lab_description: "" lab_title: {lab.title} nodes: """ base_port = CONSOLE_BASE_PORT i = 0 ignore_nodes = ["jump-host", "Mgmt-net"] for node in lab.nodes(): if node.label in ignore_nodes or node.node_definition == "external_connector": continue self._consoles[node.label] = base_port + i config += f""" {node.id}: devices: - enabled: true listen_port: {self._consoles[node.label]} name: serial0 running: true status: "" label: {node.label} """ i += 1 config += f""" path: /etc/breakout/labs.yaml - content: | #!/bin/bash nohup /usr/bin/cml_breakout -config /etc/breakout/config.yaml -extralf -labs /etc/breakout/labs.yaml -listen 0.0.0.0 -noverify run & path: /etc/breakout/breakout.sh permissions: '0755' runcmd: - [ ip, addr, add, 192.168.1.1/24, dev, ens3 ] - [ ip, link, set, up, dev, ens3 ] - [ curl, -L, --output, /usr/bin/cml_breakout, -k, 'https://{self._host}/breakout/breakout-linux-x86_amd64' ] - [ chmod, '0555', /usr/bin/cml_breakout ] - [ /etc/breakout/breakout.sh ] """ jump_host.config = config def start_lab(self, lid): lab = self._client.join_existing_lab(lid) mnets = [] for node in lab.nodes(): if node.label != "jump-host": node.start() if node.node_definition == "external_connector": mnets.append(node) if len(mnets) > 0: ready = False while not ready: for n in mnets: if not n.is_booted(): ready = False break ready = True time.sleep(1) self._configure_breakout(lab) jump_host = lab.get_node_by_label("jump-host") jump_host.start() while not jump_host.is_booted(): time.sleep(1) def get_lab_address(self, lid): lab = self._client.join_existing_lab(lid) jump_host = lab.get_node_by_label("jump-host") mgmtip = None retries = 0 while mgmtip is None: if retries > 10: break for i in jump_host.interfaces(): if i.discovered_ipv4 and len(i.discovered_ipv4) > 0: mgmtip = i.discovered_ipv4[0] break retries += 1 time.sleep(1) return mgmtip def get_lab_consoles(self): if len(self._consoles) == 0: raise Exception("ERROR: Consoles have not been generated yet") return self._consoles @staticmethod def _extract_configuration_task(node, pylab): if node.is_booted() and node.label != "jump-host": # We do this to ensure the extract is at the enable prompt. try: pylab.run_command(node.label, "show version") except Exception: pass node.extract_configuration() def archive_lab(self, lid, filename, node_password): try: lab = self._client.join_existing_lab(lid) except LabNotFound: return # The client library prints "API Error" warnings when a node doesn't support extraction. Quiet these. logger = logging.getLogger("virl2_client.models.authentication") level = logger.getEffectiveLevel() logger.setLevel(logging.CRITICAL) pylab = ClPyats(lab) os.environ["PYATS_USERNAME"] = node_password os.environ["PYATS_PASSWORD"] = node_password os.environ["PYATS_AUTH_PASS"] = node_password pylab.sync_testbed(self._username, self._password) with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor: future_nodes = { executor.submit(CML._extract_configuration_task, node, pylab): node for node in lab.nodes() } for fn in concurrent.futures.as_completed(future_nodes): try: fn.result() except Exception: pass logger.setLevel(level) with open(filename, "w") as fd: fd.write(lab.download()) def remove_lab(self, lid): try: lab = self._client.join_existing_lab(lid) except LabNotFound: return lab.stop(wait=True) lab.wipe(wait=True) lab.remove() def get_student(self, student): session = self._client.session try: r = session.get(f"{self._client._base_url}users/{student}") r.raise_for_status() except Exception as e: if r.status_code == 404: return None raise Exception(f"ERROR: Failed to query for student: {e}") else: return r.json() def add_student(self, student, name, password): session = self._client.session try: payload = {"password": password, "fullname": name} r = session.post(f"{self._client._base_url}users/{student}", json=payload) r.raise_for_status() except Exception as e: raise Exception(f"ERROR: Failed to add new student: {e}") def remove_student(self, student): session = self._client.session try: r = session.delete(f"{self._client._base_url}users/{student}") r.raise_for_status() except Exception as e: raise Exception(f"ERROR: Failed to remove student: {e}")
if args.action == 'create': lab = import_lab(client_library, args.topology) f = open(".lab_id", "w") f.write(lab.id) f.close() print('Starting ' + lab.title + '...') lab.start(wait=True) print('Done!') if args.action == 'destroy': f = open(".lab_id", "r") lab_id = f.read() f.close() print('Lab ID is ' + lab_id) lab = client_library.join_existing_lab(lab_id) print('Stoping lab' + lab_id) lab.stop() print('Wiping lab' + lab_id + ' out') lab.wipe() print('Removing lab' + lab_id) client_library.remove_lab(lab_id) print('Done!')
class virlModule(object): def __init__(self, module, function=None): self.module = module self.params = module.params self.result = dict(changed=False) self.headers = dict() self.function = function self.cookies = None self.json = None self.method = None self.path = None self.response = None self.status = None self.url = None self.params['force_basic_auth'] = True self.user = self.params['user'] self.password = self.params['password'] self.host = self.params['host'] self.timeout = self.params['timeout'] self.modifiable_methods = ['POST', 'PUT', 'DELETE'] self.client = None self.login() def login(self): self.client = ClientLibrary('https://{0}'.format(self.host), self.user, self.password, ssl_verify=False) def get_lab_by_name(self, name): for lab in self.client.all_labs(): if lab.name == name: return lab return None def get_node_by_name(self, lab, name): for node in lab.nodes(): if node.label == name: return node return None def get_lab_by_id(self, lab_id): try: lab = self.client.join_existing_lab(lab_id, sync_lab=True) except Exception: lab = None return lab def exit_json(self, **kwargs): self.result.update(**kwargs) self.module.exit_json(**self.result) def fail_json(self, msg, **kwargs): self.result.update(**kwargs) self.module.fail_json(msg=msg, **self.result)
cml_username, cml_password, ssl_verify=False, raise_for_auth_failure=True, allow_http=True) #get lab lab = client.find_labs_by_title("Jupyter Lab")[0] #get lab testbed pyats_testbed = lab.get_pyats_testbed() #Add credentials to yaml file doc = yaml.load(pyats_testbed) doc["devices"]["terminal_server"]["connections"]["cli"][ "username"] = cml_username doc["devices"]["terminal_server"]["connections"]["cli"][ "password"] = cml_password # Write the YAML testbed out to a file with open("lab_testbed.yaml", "w") as f: yaml.dump(doc, f) #run command in a node lab = client.join_existing_lab(lab.id) print("Device -> " + str(lab.nodes()[0])) node = lab.get_node_by_id(lab.nodes()[0].id) lab.pyats.sync_testbed(cml_username, cml_password) version = node.run_pyats_command("show config") print(version)