From 50dcd2d18860d761bcff96be53ae2ea82b954936 Mon Sep 17 00:00:00 2001 From: dweinholz Date: Thu, 2 Nov 2023 11:57:43 +0100 Subject: [PATCH 1/2] feat(Ports):added function to open ports for specific vms in a project --- portal_client.thrift | 11 + simple_vm_client/VirtualMachineHandler.py | 10 + simple_vm_client/VirtualMachineService-remote | 27 + simple_vm_client/VirtualMachineService.py | 611 ++++++++++++++++++ .../openstack_connector.py | 71 ++ simple_vm_client/ttypes.py | 135 ++++ 6 files changed, 865 insertions(+) diff --git a/portal_client.thrift b/portal_client.thrift index f4e43c7..306fd48 100644 --- a/portal_client.thrift +++ b/portal_client.thrift @@ -233,6 +233,11 @@ exception ServerNotFoundException { 2: string name_or_id } +exception SecurityGroupRuleNotFoundException { + /** Server not found. */ + 1: string message + 2: string name_or_id +} exception FlavorNotFoundException { 1: string message @@ -388,6 +393,12 @@ service VirtualMachineService { void resize_volume(1:string volume_id,2:int size) throws(1:VolumeNotFoundException v) + /** + * Creates/Updates a security group for a vm with a specific port range for a project + */ + string open_port_range_for_vm_in_project(1:int range_start,2:int range_stop,3:string openstack_id,4: string ethertype = "IPv4") throws (1:ServerNotFoundException e,2: DefaultException v,3:OpenStackConflictException o) + + void delete_security_group_rule(1:string openstack_id) throws (1:SecurityGroupRuleNotFoundException e) /** diff --git a/simple_vm_client/VirtualMachineHandler.py b/simple_vm_client/VirtualMachineHandler.py index 789d715..e9dbd70 100644 --- a/simple_vm_client/VirtualMachineHandler.py +++ b/simple_vm_client/VirtualMachineHandler.py @@ -299,6 +299,16 @@ def delete_user_from_backend(self, backend_id: str, user_id: str) -> dict[str, s def get_allowed_templates(self) -> list[ResearchEnvironmentTemplate]: return self.forc_connector.template.get_allowed_templates() + def open_port_range_for_vm_in_project( + self, range_start, range_stop, openstack_id, ethertype: str = "IPv4" + ) -> str: + return self.openstack_connector.open_port_range_for_vm_in_project( + range_start=range_start, + range_stop=range_stop, + openstack_id=openstack_id, + ethertype=ethertype, + ) + def add_udp_security_group(self, server_id: str) -> None: return self.openstack_connector.add_udp_security_group(server_id=server_id) diff --git a/simple_vm_client/VirtualMachineService-remote b/simple_vm_client/VirtualMachineService-remote index 8419b7f..3a64b24 100755 --- a/simple_vm_client/VirtualMachineService-remote +++ b/simple_vm_client/VirtualMachineService-remote @@ -44,6 +44,10 @@ if len(sys.argv) <= 1 or sys.argv[1] == "--help": print(" Volume get_volume(string volume_id)") print(" get_volumes_by_ids( volume_ids)") print(" void resize_volume(string volume_id, int size)") + print( + " string open_port_range_for_vm_in_project(int range_start, int range_stop, string openstack_id, string ethertype)" + ) + print(" void delete_security_group_rule(string openstack_id)") print(" void delete_server(string openstack_id)") print( " string start_server(string flavor_name, string image_name, string public_key, string servername, metadata, volume_ids_path_new, volume_ids_path_attach, additional_keys, string research_environment, additional_security_group_ids)" @@ -334,6 +338,29 @@ elif cmd == "resize_volume": ) ) +elif cmd == "open_port_range_for_vm_in_project": + if len(args) != 4: + print("open_port_range_for_vm_in_project requires 4 args") + sys.exit(1) + pp.pprint( + client.open_port_range_for_vm_in_project( + eval(args[0]), + eval(args[1]), + args[2], + args[3], + ) + ) + +elif cmd == "delete_security_group_rule": + if len(args) != 1: + print("delete_security_group_rule requires 1 args") + sys.exit(1) + pp.pprint( + client.delete_security_group_rule( + args[0], + ) + ) + elif cmd == "delete_server": if len(args) != 1: print("delete_server requires 1 args") diff --git a/simple_vm_client/VirtualMachineService.py b/simple_vm_client/VirtualMachineService.py index 814e7bf..6bdec76 100644 --- a/simple_vm_client/VirtualMachineService.py +++ b/simple_vm_client/VirtualMachineService.py @@ -148,6 +148,27 @@ def resize_volume(self, volume_id, size): """ + def open_port_range_for_vm_in_project( + self, range_start, range_stop, openstack_id, ethertype + ): + """ + Creates/Updates a security group for a vm with a specific port range for a project + + Parameters: + - range_start + - range_stop + - openstack_id + - ethertype + + """ + + def delete_security_group_rule(self, openstack_id): + """ + Parameters: + - openstack_id + + """ + def delete_server(self, openstack_id): """ Delete server. @@ -1230,6 +1251,95 @@ def recv_resize_volume(self): raise result.v return + def open_port_range_for_vm_in_project( + self, range_start, range_stop, openstack_id, ethertype + ): + """ + Creates/Updates a security group for a vm with a specific port range for a project + + Parameters: + - range_start + - range_stop + - openstack_id + - ethertype + + """ + self.send_open_port_range_for_vm_in_project( + range_start, range_stop, openstack_id, ethertype + ) + return self.recv_open_port_range_for_vm_in_project() + + def send_open_port_range_for_vm_in_project( + self, range_start, range_stop, openstack_id, ethertype + ): + self._oprot.writeMessageBegin( + "open_port_range_for_vm_in_project", TMessageType.CALL, self._seqid + ) + args = open_port_range_for_vm_in_project_args() + args.range_start = range_start + args.range_stop = range_stop + args.openstack_id = openstack_id + args.ethertype = ethertype + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_open_port_range_for_vm_in_project(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = open_port_range_for_vm_in_project_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + if result.e is not None: + raise result.e + if result.v is not None: + raise result.v + raise TApplicationException( + TApplicationException.MISSING_RESULT, + "open_port_range_for_vm_in_project failed: unknown result", + ) + + def delete_security_group_rule(self, openstack_id): + """ + Parameters: + - openstack_id + + """ + self.send_delete_security_group_rule(openstack_id) + self.recv_delete_security_group_rule() + + def send_delete_security_group_rule(self, openstack_id): + self._oprot.writeMessageBegin( + "delete_security_group_rule", TMessageType.CALL, self._seqid + ) + args = delete_security_group_rule_args() + args.openstack_id = openstack_id + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_delete_security_group_rule(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = delete_security_group_rule_result() + result.read(iprot) + iprot.readMessageEnd() + if result.e is not None: + raise result.e + return + def delete_server(self, openstack_id): """ Delete server. @@ -3277,6 +3387,12 @@ def __init__(self, handler): self._processMap["get_volume"] = Processor.process_get_volume self._processMap["get_volumes_by_ids"] = Processor.process_get_volumes_by_ids self._processMap["resize_volume"] = Processor.process_resize_volume + self._processMap[ + "open_port_range_for_vm_in_project" + ] = Processor.process_open_port_range_for_vm_in_project + self._processMap[ + "delete_security_group_rule" + ] = Processor.process_delete_security_group_rule self._processMap["delete_server"] = Processor.process_delete_server self._processMap["start_server"] = Processor.process_start_server self._processMap[ @@ -3805,6 +3921,67 @@ def process_resize_volume(self, seqid, iprot, oprot): oprot.writeMessageEnd() oprot.trans.flush() + def process_open_port_range_for_vm_in_project(self, seqid, iprot, oprot): + args = open_port_range_for_vm_in_project_args() + args.read(iprot) + iprot.readMessageEnd() + result = open_port_range_for_vm_in_project_result() + try: + result.success = self._handler.open_port_range_for_vm_in_project( + args.range_start, args.range_stop, args.openstack_id, args.ethertype + ) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except ServerNotFoundException as e: + msg_type = TMessageType.REPLY + result.e = e + except DefaultException as v: + msg_type = TMessageType.REPLY + result.v = v + except TApplicationException as ex: + logging.exception("TApplication exception in handler") + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception("Unexpected exception in handler") + msg_type = TMessageType.EXCEPTION + result = TApplicationException( + TApplicationException.INTERNAL_ERROR, "Internal error" + ) + oprot.writeMessageBegin("open_port_range_for_vm_in_project", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_delete_security_group_rule(self, seqid, iprot, oprot): + args = delete_security_group_rule_args() + args.read(iprot) + iprot.readMessageEnd() + result = delete_security_group_rule_result() + try: + self._handler.delete_security_group_rule(args.openstack_id) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except SecurityGroupRuleNotFoundException as e: + msg_type = TMessageType.REPLY + result.e = e + except TApplicationException as ex: + logging.exception("TApplication exception in handler") + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception("Unexpected exception in handler") + msg_type = TMessageType.EXCEPTION + result = TApplicationException( + TApplicationException.INTERNAL_ERROR, "Internal error" + ) + oprot.writeMessageBegin("delete_security_group_rule", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + def process_delete_server(self, seqid, iprot, oprot): args = delete_server_args() args.read(iprot) @@ -7887,6 +8064,440 @@ def __ne__(self, other): ) +class open_port_range_for_vm_in_project_args(object): + """ + Attributes: + - range_start + - range_stop + - openstack_id + - ethertype + + """ + + def __init__( + self, + range_start=None, + range_stop=None, + openstack_id=None, + ethertype="IPv4", + ): + self.range_start = range_start + self.range_stop = range_stop + self.openstack_id = openstack_id + self.ethertype = ethertype + + def read(self, iprot): + if ( + iprot._fast_decode is not None + and isinstance(iprot.trans, TTransport.CReadableTransport) + and self.thrift_spec is not None + ): + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.range_start = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.range_stop = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.openstack_id = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.ethertype = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write( + oprot._fast_encode(self, [self.__class__, self.thrift_spec]) + ) + return + oprot.writeStructBegin("open_port_range_for_vm_in_project_args") + if self.range_start is not None: + oprot.writeFieldBegin("range_start", TType.I32, 1) + oprot.writeI32(self.range_start) + oprot.writeFieldEnd() + if self.range_stop is not None: + oprot.writeFieldBegin("range_stop", TType.I32, 2) + oprot.writeI32(self.range_stop) + oprot.writeFieldEnd() + if self.openstack_id is not None: + oprot.writeFieldBegin("openstack_id", TType.STRING, 3) + oprot.writeString( + self.openstack_id.encode("utf-8") + if sys.version_info[0] == 2 + else self.openstack_id + ) + oprot.writeFieldEnd() + if self.ethertype is not None: + oprot.writeFieldBegin("ethertype", TType.STRING, 4) + oprot.writeString( + self.ethertype.encode("utf-8") + if sys.version_info[0] == 2 + else self.ethertype + ) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ["%s=%r" % (key, value) for key, value in self.__dict__.items()] + return "%s(%s)" % (self.__class__.__name__, ", ".join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +all_structs.append(open_port_range_for_vm_in_project_args) +open_port_range_for_vm_in_project_args.thrift_spec = ( + None, # 0 + ( + 1, + TType.I32, + "range_start", + None, + None, + ), # 1 + ( + 2, + TType.I32, + "range_stop", + None, + None, + ), # 2 + ( + 3, + TType.STRING, + "openstack_id", + "UTF8", + None, + ), # 3 + ( + 4, + TType.STRING, + "ethertype", + "UTF8", + "IPv4", + ), # 4 +) + + +class open_port_range_for_vm_in_project_result(object): + """ + Attributes: + - success + - e + - v + + """ + + def __init__( + self, + success=None, + e=None, + v=None, + ): + self.success = success + self.e = e + self.v = v + + def read(self, iprot): + if ( + iprot._fast_decode is not None + and isinstance(iprot.trans, TTransport.CReadableTransport) + and self.thrift_spec is not None + ): + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRING: + self.success = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + elif fid == 1: + if ftype == TType.STRUCT: + self.e = ServerNotFoundException.read(iprot) + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRUCT: + self.v = DefaultException.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write( + oprot._fast_encode(self, [self.__class__, self.thrift_spec]) + ) + return + oprot.writeStructBegin("open_port_range_for_vm_in_project_result") + if self.success is not None: + oprot.writeFieldBegin("success", TType.STRING, 0) + oprot.writeString( + self.success.encode("utf-8") + if sys.version_info[0] == 2 + else self.success + ) + oprot.writeFieldEnd() + if self.e is not None: + oprot.writeFieldBegin("e", TType.STRUCT, 1) + self.e.write(oprot) + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin("v", TType.STRUCT, 2) + self.v.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ["%s=%r" % (key, value) for key, value in self.__dict__.items()] + return "%s(%s)" % (self.__class__.__name__, ", ".join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +all_structs.append(open_port_range_for_vm_in_project_result) +open_port_range_for_vm_in_project_result.thrift_spec = ( + ( + 0, + TType.STRING, + "success", + "UTF8", + None, + ), # 0 + ( + 1, + TType.STRUCT, + "e", + [ServerNotFoundException, None], + None, + ), # 1 + ( + 2, + TType.STRUCT, + "v", + [DefaultException, None], + None, + ), # 2 +) + + +class delete_security_group_rule_args(object): + """ + Attributes: + - openstack_id + + """ + + def __init__( + self, + openstack_id=None, + ): + self.openstack_id = openstack_id + + def read(self, iprot): + if ( + iprot._fast_decode is not None + and isinstance(iprot.trans, TTransport.CReadableTransport) + and self.thrift_spec is not None + ): + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.openstack_id = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write( + oprot._fast_encode(self, [self.__class__, self.thrift_spec]) + ) + return + oprot.writeStructBegin("delete_security_group_rule_args") + if self.openstack_id is not None: + oprot.writeFieldBegin("openstack_id", TType.STRING, 1) + oprot.writeString( + self.openstack_id.encode("utf-8") + if sys.version_info[0] == 2 + else self.openstack_id + ) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ["%s=%r" % (key, value) for key, value in self.__dict__.items()] + return "%s(%s)" % (self.__class__.__name__, ", ".join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +all_structs.append(delete_security_group_rule_args) +delete_security_group_rule_args.thrift_spec = ( + None, # 0 + ( + 1, + TType.STRING, + "openstack_id", + "UTF8", + None, + ), # 1 +) + + +class delete_security_group_rule_result(object): + """ + Attributes: + - e + + """ + + def __init__( + self, + e=None, + ): + self.e = e + + def read(self, iprot): + if ( + iprot._fast_decode is not None + and isinstance(iprot.trans, TTransport.CReadableTransport) + and self.thrift_spec is not None + ): + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRUCT: + self.e = SecurityGroupRuleNotFoundException.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write( + oprot._fast_encode(self, [self.__class__, self.thrift_spec]) + ) + return + oprot.writeStructBegin("delete_security_group_rule_result") + if self.e is not None: + oprot.writeFieldBegin("e", TType.STRUCT, 1) + self.e.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ["%s=%r" % (key, value) for key, value in self.__dict__.items()] + return "%s(%s)" % (self.__class__.__name__, ", ".join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +all_structs.append(delete_security_group_rule_result) +delete_security_group_rule_result.thrift_spec = ( + None, # 0 + ( + 1, + TType.STRUCT, + "e", + [SecurityGroupRuleNotFoundException, None], + None, + ), # 1 +) + + class delete_server_args(object): """ Attributes: diff --git a/simple_vm_client/openstack_connector/openstack_connector.py b/simple_vm_client/openstack_connector/openstack_connector.py index 3cf2cdc..2ba17a6 100644 --- a/simple_vm_client/openstack_connector/openstack_connector.py +++ b/simple_vm_client/openstack_connector/openstack_connector.py @@ -704,6 +704,65 @@ def create_or_get_default_ssh_security_group(self): description="Default SSH SimpleVM Security Group", ) + def open_port_range_for_vm_in_project( + self, range_start, range_stop, openstack_id, ethertype="IPV4" + ): + server: Server = self.openstack_connection.get_server_by_id(id=openstack_id) + if server is None: + logger.exception(f"Instance {openstack_id} not found") + raise ServerNotFoundException( + message=f"Instance {openstack_id} not found", + name_or_id=openstack_id, + ) + project_name = server.metadata.get("project_name") + project_id = server.metadata.get("project_id") + + project_security_group = self.get_or_create_project_security_group( + project_name=project_name, project_id=project_id + ) + vm_security_group = self.get_or_create_vm_security_group( + openstack_id=openstack_id + ) + current_vm_security_group_names = [ + sec["name"] for sec in server["security_groups"] + ] + if openstack_id not in current_vm_security_group_names: + self.openstack_connection.add_server_security_groups( + server=server, security_groups=[vm_security_group] + ) + if ethertype not in ["IPv4", "IPv6"]: + raise DefaultException( + message=f"Type {ethertype} does not exist for security group rules" + ) + + try: + if ethertype == "IPv4": + security_rule = self.openstack_connection.create_security_group_rule( + direction="ingress", + protocol="tcp", + port_range_max=range_stop, + port_range_min=range_start, + secgroup_name_or_id=vm_security_group, + remote_group_id=project_security_group, + ) + return security_rule["id"] + elif ethertype == "IPv6": + security_rule = self.openstack_connection.create_security_group_rule( + direction="ingress", + ethertype="IPv6", + protocol="tcp", + port_range_max=range_stop, + port_range_min=range_start, + secgroup_name_or_id=vm_security_group, + remote_group_id=project_security_group, + ) + return security_rule["id"] + except ConflictException as e: + logger.exception( + f"Could not create security group rule for instance {openstack_id}" + ) + raise OpenStackConflictException(message=e.message) + def create_security_group( self, name: str, @@ -851,6 +910,18 @@ def get_or_create_research_environment_security_group( ) return new_security_group["id"] + def get_or_create_vm_security_group(self, openstack_id): + logger.info(f"Check if Security Group for vm - [{openstack_id}] exists... ") + sec = self.openstack_connection.get_security_group(name_or_id=openstack_id) + if sec: + logger.info(f"Security group [{openstack_id}] already exists.") + return sec["id"] + logger.info(f"No security Group for [{openstack_id}] exists. Creating.. ") + new_security_group = self.openstack_connection.create_security_group( + name=openstack_id, description=f"VM ID: {openstack_id} Security Group" + ) + return new_security_group["id"] + def get_or_create_project_security_group(self, project_name, project_id): security_group_name = f"{project_name}_{project_id}" logger.info( diff --git a/simple_vm_client/ttypes.py b/simple_vm_client/ttypes.py index e24da55..d1230a8 100644 --- a/simple_vm_client/ttypes.py +++ b/simple_vm_client/ttypes.py @@ -2665,6 +2665,123 @@ def __ne__(self, other): return not (self == other) +class SecurityGroupRuleNotFoundException(TException): + """ + Attributes: + - message: Server not found. + - name_or_id + + """ + + def __init__( + self, + message=None, + name_or_id=None, + ): + super(SecurityGroupRuleNotFoundException, self).__setattr__("message", message) + super(SecurityGroupRuleNotFoundException, self).__setattr__( + "name_or_id", name_or_id + ) + + def __setattr__(self, *args): + raise TypeError("can't modify immutable instance") + + def __delattr__(self, *args): + raise TypeError("can't modify immutable instance") + + def __hash__(self): + return hash(self.__class__) ^ hash( + ( + self.message, + self.name_or_id, + ) + ) + + @classmethod + def read(cls, iprot): + if ( + iprot._fast_decode is not None + and isinstance(iprot.trans, TTransport.CReadableTransport) + and cls.thrift_spec is not None + ): + return iprot._fast_decode(None, iprot, [cls, cls.thrift_spec]) + iprot.readStructBegin() + message = None + name_or_id = None + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + message = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + name_or_id = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + return cls( + message=message, + name_or_id=name_or_id, + ) + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write( + oprot._fast_encode(self, [self.__class__, self.thrift_spec]) + ) + return + oprot.writeStructBegin("SecurityGroupRuleNotFoundException") + if self.message is not None: + oprot.writeFieldBegin("message", TType.STRING, 1) + oprot.writeString( + self.message.encode("utf-8") + if sys.version_info[0] == 2 + else self.message + ) + oprot.writeFieldEnd() + if self.name_or_id is not None: + oprot.writeFieldBegin("name_or_id", TType.STRING, 2) + oprot.writeString( + self.name_or_id.encode("utf-8") + if sys.version_info[0] == 2 + else self.name_or_id + ) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __str__(self): + return repr(self) + + def __repr__(self): + L = ["%s=%r" % (key, value) for key, value in self.__dict__.items()] + return "%s(%s)" % (self.__class__.__name__, ", ".join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + class FlavorNotFoundException(TException): """ Attributes: @@ -4342,6 +4459,24 @@ def __ne__(self, other): None, ), # 2 ) +all_structs.append(SecurityGroupRuleNotFoundException) +SecurityGroupRuleNotFoundException.thrift_spec = ( + None, # 0 + ( + 1, + TType.STRING, + "message", + "UTF8", + None, + ), # 1 + ( + 2, + TType.STRING, + "name_or_id", + "UTF8", + None, + ), # 2 +) all_structs.append(FlavorNotFoundException) FlavorNotFoundException.thrift_spec = ( None, # 0 From 9b207c694d45a3a85c15898bdd2cd7e9fdbddc13 Mon Sep 17 00:00:00 2001 From: dweinholz Date: Thu, 2 Nov 2023 15:50:34 +0100 Subject: [PATCH 2/2] feat(Ports):added removing --- portal_client.thrift | 4 +- simple_vm_client/VirtualMachineHandler.py | 13 +++- simple_vm_client/VirtualMachineService-remote | 7 +- simple_vm_client/VirtualMachineService.py | 68 +++++++++++++++++-- .../openstack_connector.py | 47 ++++++------- 5 files changed, 105 insertions(+), 34 deletions(-) diff --git a/portal_client.thrift b/portal_client.thrift index 306fd48..5c5bb8c 100644 --- a/portal_client.thrift +++ b/portal_client.thrift @@ -396,9 +396,9 @@ service VirtualMachineService { /** * Creates/Updates a security group for a vm with a specific port range for a project */ - string open_port_range_for_vm_in_project(1:int range_start,2:int range_stop,3:string openstack_id,4: string ethertype = "IPv4") throws (1:ServerNotFoundException e,2: DefaultException v,3:OpenStackConflictException o) + string open_port_range_for_vm_in_project(1:int range_start,2:int range_stop,3:string openstack_id,4: string ethertype = "IPv4",5:string protocol ="TCP") throws (1:ServerNotFoundException e,2: DefaultException v,3:OpenStackConflictException o) - void delete_security_group_rule(1:string openstack_id) throws (1:SecurityGroupRuleNotFoundException e) + void delete_security_group_rule(1:string openstack_id) throws (1:SecurityGroupRuleNotFoundException e,2:DefaultException f) /** diff --git a/simple_vm_client/VirtualMachineHandler.py b/simple_vm_client/VirtualMachineHandler.py index e9dbd70..547fd58 100644 --- a/simple_vm_client/VirtualMachineHandler.py +++ b/simple_vm_client/VirtualMachineHandler.py @@ -299,14 +299,25 @@ def delete_user_from_backend(self, backend_id: str, user_id: str) -> dict[str, s def get_allowed_templates(self) -> list[ResearchEnvironmentTemplate]: return self.forc_connector.template.get_allowed_templates() + def delete_security_group_rule(self, openstack_id): + return self.openstack_connector.delete_security_group_rule( + openstack_id=openstack_id + ) + def open_port_range_for_vm_in_project( - self, range_start, range_stop, openstack_id, ethertype: str = "IPv4" + self, + range_start, + range_stop, + openstack_id, + ethertype: str = "IPv4", + protocol: str = "TCP", ) -> str: return self.openstack_connector.open_port_range_for_vm_in_project( range_start=range_start, range_stop=range_stop, openstack_id=openstack_id, ethertype=ethertype, + protocol=protocol, ) def add_udp_security_group(self, server_id: str) -> None: diff --git a/simple_vm_client/VirtualMachineService-remote b/simple_vm_client/VirtualMachineService-remote index 3a64b24..9c49441 100755 --- a/simple_vm_client/VirtualMachineService-remote +++ b/simple_vm_client/VirtualMachineService-remote @@ -45,7 +45,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == "--help": print(" get_volumes_by_ids( volume_ids)") print(" void resize_volume(string volume_id, int size)") print( - " string open_port_range_for_vm_in_project(int range_start, int range_stop, string openstack_id, string ethertype)" + " string open_port_range_for_vm_in_project(int range_start, int range_stop, string openstack_id, string ethertype, string protocol)" ) print(" void delete_security_group_rule(string openstack_id)") print(" void delete_server(string openstack_id)") @@ -339,8 +339,8 @@ elif cmd == "resize_volume": ) elif cmd == "open_port_range_for_vm_in_project": - if len(args) != 4: - print("open_port_range_for_vm_in_project requires 4 args") + if len(args) != 5: + print("open_port_range_for_vm_in_project requires 5 args") sys.exit(1) pp.pprint( client.open_port_range_for_vm_in_project( @@ -348,6 +348,7 @@ elif cmd == "open_port_range_for_vm_in_project": eval(args[1]), args[2], args[3], + args[4], ) ) diff --git a/simple_vm_client/VirtualMachineService.py b/simple_vm_client/VirtualMachineService.py index 6bdec76..461f7e0 100644 --- a/simple_vm_client/VirtualMachineService.py +++ b/simple_vm_client/VirtualMachineService.py @@ -149,7 +149,7 @@ def resize_volume(self, volume_id, size): """ def open_port_range_for_vm_in_project( - self, range_start, range_stop, openstack_id, ethertype + self, range_start, range_stop, openstack_id, ethertype, protocol ): """ Creates/Updates a security group for a vm with a specific port range for a project @@ -159,6 +159,7 @@ def open_port_range_for_vm_in_project( - range_stop - openstack_id - ethertype + - protocol """ @@ -1252,7 +1253,7 @@ def recv_resize_volume(self): return def open_port_range_for_vm_in_project( - self, range_start, range_stop, openstack_id, ethertype + self, range_start, range_stop, openstack_id, ethertype, protocol ): """ Creates/Updates a security group for a vm with a specific port range for a project @@ -1262,15 +1263,16 @@ def open_port_range_for_vm_in_project( - range_stop - openstack_id - ethertype + - protocol """ self.send_open_port_range_for_vm_in_project( - range_start, range_stop, openstack_id, ethertype + range_start, range_stop, openstack_id, ethertype, protocol ) return self.recv_open_port_range_for_vm_in_project() def send_open_port_range_for_vm_in_project( - self, range_start, range_stop, openstack_id, ethertype + self, range_start, range_stop, openstack_id, ethertype, protocol ): self._oprot.writeMessageBegin( "open_port_range_for_vm_in_project", TMessageType.CALL, self._seqid @@ -1280,6 +1282,7 @@ def send_open_port_range_for_vm_in_project( args.range_stop = range_stop args.openstack_id = openstack_id args.ethertype = ethertype + args.protocol = protocol args.write(self._oprot) self._oprot.writeMessageEnd() self._oprot.trans.flush() @@ -1301,6 +1304,8 @@ def recv_open_port_range_for_vm_in_project(self): raise result.e if result.v is not None: raise result.v + if result.o is not None: + raise result.o raise TApplicationException( TApplicationException.MISSING_RESULT, "open_port_range_for_vm_in_project failed: unknown result", @@ -3928,7 +3933,11 @@ def process_open_port_range_for_vm_in_project(self, seqid, iprot, oprot): result = open_port_range_for_vm_in_project_result() try: result.success = self._handler.open_port_range_for_vm_in_project( - args.range_start, args.range_stop, args.openstack_id, args.ethertype + args.range_start, + args.range_stop, + args.openstack_id, + args.ethertype, + args.protocol, ) msg_type = TMessageType.REPLY except TTransport.TTransportException: @@ -3939,6 +3948,9 @@ def process_open_port_range_for_vm_in_project(self, seqid, iprot, oprot): except DefaultException as v: msg_type = TMessageType.REPLY result.v = v + except OpenStackConflictException as o: + msg_type = TMessageType.REPLY + result.o = o except TApplicationException as ex: logging.exception("TApplication exception in handler") msg_type = TMessageType.EXCEPTION @@ -8071,6 +8083,7 @@ class open_port_range_for_vm_in_project_args(object): - range_stop - openstack_id - ethertype + - protocol """ @@ -8080,11 +8093,13 @@ def __init__( range_stop=None, openstack_id=None, ethertype="IPv4", + protocol="TCP", ): self.range_start = range_start self.range_stop = range_stop self.openstack_id = openstack_id self.ethertype = ethertype + self.protocol = protocol def read(self, iprot): if ( @@ -8127,6 +8142,15 @@ def read(self, iprot): ) else: iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.protocol = ( + iprot.readString().decode("utf-8", errors="replace") + if sys.version_info[0] == 2 + else iprot.readString() + ) + else: + iprot.skip(ftype) else: iprot.skip(ftype) iprot.readFieldEnd() @@ -8163,6 +8187,14 @@ def write(self, oprot): else self.ethertype ) oprot.writeFieldEnd() + if self.protocol is not None: + oprot.writeFieldBegin("protocol", TType.STRING, 5) + oprot.writeString( + self.protocol.encode("utf-8") + if sys.version_info[0] == 2 + else self.protocol + ) + oprot.writeFieldEnd() oprot.writeFieldStop() oprot.writeStructEnd() @@ -8211,6 +8243,13 @@ def __ne__(self, other): "UTF8", "IPv4", ), # 4 + ( + 5, + TType.STRING, + "protocol", + "UTF8", + "TCP", + ), # 5 ) @@ -8220,6 +8259,7 @@ class open_port_range_for_vm_in_project_result(object): - success - e - v + - o """ @@ -8228,10 +8268,12 @@ def __init__( success=None, e=None, v=None, + o=None, ): self.success = success self.e = e self.v = v + self.o = o def read(self, iprot): if ( @@ -8265,6 +8307,11 @@ def read(self, iprot): self.v = DefaultException.read(iprot) else: iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRUCT: + self.o = OpenStackConflictException.read(iprot) + else: + iprot.skip(ftype) else: iprot.skip(ftype) iprot.readFieldEnd() @@ -8293,6 +8340,10 @@ def write(self, oprot): oprot.writeFieldBegin("v", TType.STRUCT, 2) self.v.write(oprot) oprot.writeFieldEnd() + if self.o is not None: + oprot.writeFieldBegin("o", TType.STRUCT, 3) + self.o.write(oprot) + oprot.writeFieldEnd() oprot.writeFieldStop() oprot.writeStructEnd() @@ -8333,6 +8384,13 @@ def __ne__(self, other): [DefaultException, None], None, ), # 2 + ( + 3, + TType.STRUCT, + "o", + [OpenStackConflictException, None], + None, + ), # 3 ) diff --git a/simple_vm_client/openstack_connector/openstack_connector.py b/simple_vm_client/openstack_connector/openstack_connector.py index 2ba17a6..b8f036b 100644 --- a/simple_vm_client/openstack_connector/openstack_connector.py +++ b/simple_vm_client/openstack_connector/openstack_connector.py @@ -704,8 +704,19 @@ def create_or_get_default_ssh_security_group(self): description="Default SSH SimpleVM Security Group", ) + def delete_security_group_rule(self, openstack_id): + logger.info(f"Delete Security Group Rule -- {openstack_id}") + deleted = self.openstack_connection.delete_security_group_rule( + rule_id=openstack_id + ) + logger.info(f"Delete Result -- {deleted}") + if not deleted: + raise DefaultException( + message=f"Could not delete security group rule - {openstack_id}" + ) + def open_port_range_for_vm_in_project( - self, range_start, range_stop, openstack_id, ethertype="IPV4" + self, range_start, range_stop, openstack_id, ethertype="IPV4", protocol="TCP" ): server: Server = self.openstack_connection.get_server_by_id(id=openstack_id) if server is None: @@ -736,27 +747,17 @@ def open_port_range_for_vm_in_project( ) try: - if ethertype == "IPv4": - security_rule = self.openstack_connection.create_security_group_rule( - direction="ingress", - protocol="tcp", - port_range_max=range_stop, - port_range_min=range_start, - secgroup_name_or_id=vm_security_group, - remote_group_id=project_security_group, - ) - return security_rule["id"] - elif ethertype == "IPv6": - security_rule = self.openstack_connection.create_security_group_rule( - direction="ingress", - ethertype="IPv6", - protocol="tcp", - port_range_max=range_stop, - port_range_min=range_start, - secgroup_name_or_id=vm_security_group, - remote_group_id=project_security_group, - ) - return security_rule["id"] + security_rule = self.openstack_connection.create_security_group_rule( + direction="ingress", + ethertype=ethertype, + protocol=protocol, + port_range_max=range_stop, + port_range_min=range_start, + secgroup_name_or_id=vm_security_group, + remote_group_id=project_security_group, + ) + return security_rule["id"] + except ConflictException as e: logger.exception( f"Could not create security group rule for instance {openstack_id}" @@ -1112,7 +1113,7 @@ def delete_server(self, openstack_id: str) -> None: ) if ( sg["name"] != self.DEFAULT_SECURITY_GROUP_NAME - and "bibigrid" not in sec.name + and ("bibigrid" not in sec.name or "master" not in server.name) and not self.is_security_group_in_use(security_group_id=sec.id) ): self.openstack_connection.delete_security_group(sg)