Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file albatross_cli.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403(* (c) 2018 Hannes Mehnert, all rights reserved *)openAstringopenVmm_coreopenLwt.Infixletprocess=Metrics.field~doc:"name of the process""vm"Metrics.Stringletinit_influxnamedata=matchdatawith|None->()|Some(ip,port)->Logs.info(funm->m"stats connecting to %a:%d"Ipaddr.V4.ppipport);Metrics.enable_all();Metrics_lwt.init_periodic(fun()->Lwt_unix.sleep10.);Metrics_lwt.periodically(Metrics_rusage.rusage_src~tags:[]);Metrics_lwt.periodically(Metrics_rusage.kinfo_mem_src~tags:[]);letget_cache,reporter=Metrics.cache_reporter()inMetrics.set_reporterreporter;letfd=refNoneinletrecreport()=letsend()=(match!fdwith|Some_->Lwt.return_unit|None->letaddr=Lwt_unix.ADDR_INET(Ipaddr_unix.V4.to_inet_addrip,port)inVmm_lwt.connectLwt_unix.PF_INETaddr>|=function|None->Logs.err(funm->m"connection failure to stats")|Somefd'->fd:=Somefd')>>=fun()->match!fdwith|None->Lwt.return_unit|Somesocket->lettag=processnameinletdatas=Metrics.SM.fold(funsrc(tags,data)acc->letname=Metrics.Src.namesrcinMetrics_influx.encode_line_protocol(tag::tags)dataname::acc)(get_cache())[]inletdatas=String.concat~sep:""datasinVmm_lwt.write_rawsocket(Bytes.unsafe_of_stringdatas)>|=function|Ok()->()|Error`Exception->Logs.warn(funm->m"error on stats write");fd:=Noneandsleep()=Lwt_unix.sleep10.inLwt.join[send();sleep()]>>=reportinLwt.asyncreporttypeexit_status=|Success|Local_authentication_failed|Remote_authentication_failed|Communication_failed|Connect_failed|Remote_command_failed|Cli_failed|Internal_errorletoutput_result((hdr,reply)aswire)=matchreplywith|`Successs->Logs.app(funm->m"%a"Vmm_commands.pp_wirewire);letwrite_to_filenamecompresseddata=letfilename=letts=Ptime.to_rfc3339(Ptime_clock.now())inFpath.(v(Filename.get_temp_dir_name())/Name.to_stringname+ts)inletwritedata=matchBos.OS.File.writefilenamedatawith|Ok()->Logs.app(funm->m"dumped image to %a"Fpath.ppfilename)|Error(`Msge)->Logs.err(funm->m"failed to write image: %s"e)inifcompressedthenmatchVmm_compress.uncompress(Cstruct.to_stringdata)with|Okblob->writeblob|Error()->Logs.err(funm->m"failed to uncompress image")elsewrite(Cstruct.to_stringdata)inbeginmatchswith|`Unikernel_image(compressed,image)->letname=hdr.Vmm_commands.nameinwrite_to_filenamecompressedimage|`Old_unikernelsvms->List.iter(fun(name,cfg)->ifCstruct.lencfg.Unikernel.image>0thenwrite_to_filenamecfg.Unikernel.compressedcfg.Unikernel.image)vms|_->()end;Ok()|`Data_->Logs.app(funm->m"%a"Vmm_commands.pp_wirewire);Ok()|`Failure_->Logs.warn(funm->m"%a"Vmm_commands.pp_wirewire);ErrorRemote_command_failed|`Command_->Logs.err(funm->m"received unexpected command %a"Vmm_commands.pp_wirewire);ErrorInternal_errorletsetup_logstyle_rendererlevel=Fmt_tty.setup_std_outputs?style_renderer();Logs.set_levellevel;Logs.set_reporter(Logs_fmt.reporter~dst:Format.std_formatter())letcreate_vmforceimagecpuidmemoryargvblock_devicesbridgescompressionrestart_on_failexit_codes=letopenRresult.R.Infixinletimg_file=Fpath.vimageinBos.OS.File.readimg_file>>=funimage->Vmm_unix.manifest_devices_match~bridges~block_devicesimg_file>>|fun()->letimage,compressed=matchcompressionwith|0->Cstruct.of_stringimage,false|level->letimg=Vmm_compress.compress~levelimageinCstruct.of_stringimg,trueandargv=matchargvwith[]->None|xs->Somexsandfail_behaviour=letexits=matchexit_codeswith[]->None|xs->Some(IS.of_listxs)inifrestart_on_failthen`Restartexitselse`Quitinletconfig={Unikernel.typ=`Solo5;compressed;image;fail_behaviour;cpuid;memory;block_devices;bridges;argv}inifforcethen`Unikernel_force_createconfigelse`Unikernel_createconfigletpolicyvmsmemorycpusblockbridges=letbridges=String.Set.of_listbridgesandcpuids=IS.of_listcpusinPolicy.{vms;cpuids;memory;block;bridges}openCmdlinerletsetup_log=Term.(constsetup_log$Fmt_cli.style_renderer()$Logs_cli.level())letip_port:(Ipaddr.V4.t*int)Arg.converter=letdefault_port=8094inletparses=matchmatchString.cut~sep:":"swith|None->Ok(s,default_port)|Some(ip,port)->matchint_of_stringportwith|exceptionFailure_->Error"non-numeric port"|port->Ok(ip,port)with|Errormsg->`Errormsg|Ok(ip,port)->matchIpaddr.V4.of_stringipwith|Okip->`Ok(ip,port)|Error`Msgmsg->`Errormsginparse,funppf(ip,port)->Format.fprintfppf"%a:%d"Ipaddr.V4.ppipportletinflux=letdoc="IP address and port (default: 8094) to report metrics to in influx line protocol"inArg.(value&opt(someip_port)None&info["influx"]~doc~docv:"INFLUXHOST[:PORT]")lethost_port:(string*int)Arg.converter=letparses=matchString.cut~sep:":"swith|None->`Error"broken: no port specified"|Some(hostname,port)->try`Ok(hostname,int_of_stringport)withNot_found->`Error"failed to parse port"inparse,funppf(h,p)->Format.fprintfppf"%s:%d"hpletvm_c=letparses=matchName.of_stringswith|Error(`Msgmsg)->`Errormsg|Okname->`Oknamein(parse,Name.pp)letbridge_tap_c=letparses=matchAstring.String.cut~sep:":"swith|None->`Error"broken, format is bridge:tap"|Some(bridge,tap)->`Ok(bridge,tap)in(parse,funppf(bridge,tap)->Format.fprintfppf"%s:%s"bridgetap)letbridge_taps=letdoc="Bridge and tap device names"inArg.(value&opt_allbridge_tap_c[]&info["bridge"]~doc)letpid_req1=letdoc="Process id"inArg.(required&pos1(someint)None&info[]~doc~docv:"PID")letvmm_dev_req0=letdoc="VMM device name"inArg.(required&pos0(somestring)None&info[]~doc~docv:"VMMDEV")letopt_vm_name=letdoc="name of virtual machine."inArg.(value&optvm_cName.root&info["n";"name"]~doc)letcompress_leveldefault=letdoc="Compression level (0 - 9), a higher value results in smaller data, but uses more CPU "inArg.(value&optintdefault&info["compression-level"]~doc)letforce=letdoc="force VM creation."inArg.(value&flag&info["f";"force"]~doc)letcpus=letdoc="CPUids to allow"inArg.(value&opt_allint[]&info["cpu"]~doc)letvms=letdoc="Number of VMs to allow"inArg.(required&pos1(someint)None&info[]~doc~docv:"VMS")letimage=letdoc="File of virtual machine image."inArg.(required&pos1(somefile)None&info[]~doc~docv:"IMAGE")letvm_name=letdoc="Name virtual machine."inArg.(required&pos0(somevm_c)None&info[]~doc~docv:"VM")letblock_name=letdoc="Name of block device."inArg.(required&pos0(somevm_c)None&info[]~doc~docv:"BLOCK")letblock_size=letdoc="Block size in MB."inArg.(required&pos1(someint)None&info[]~doc~docv:"SIZE")letopt_block_name=letdoc="Name of block device."inArg.(value&optvm_cName.root&info["name"]~doc)letopt_block_size=letdoc="Block storage to allow in MB"inArg.(value&opt(someint)None&info["size"]~doc)letmem=letdoc="Memory to allow in MB"inArg.(value&optint512&info["mem"]~doc)letbridge=letdoc="Bridges to allow"inArg.(value&opt_allstring[]&info["bridge"]~doc)letcpu=letdoc="CPUid to use"inArg.(value&optint0&info["cpu"]~doc)letvm_mem=letdoc="Assigned memory in MB"inArg.(value&optint32&info["mem"]~doc)letargs=letdoc="Boot arguments"inArg.(value&opt_allstring[]&info["arg"]~doc)letcolon_separated_c=letparses=matchAstring.String.cut~sep:":"swith|None->`Ok(s,None)|Some(a,b)->`Ok(a,Someb)in(parse,funppf(a,b)->Fmt.pfppf"%s:%s"a(matchbwithNone->a|Someb->b))letblock=letdoc="Block device name (block or name:block-device-name)"inArg.(value&opt_allcolon_separated_c[]&info["block"]~doc)letnet=letdoc="Network device names (bridge or name:bridge)"inArg.(value&opt_allcolon_separated_c[]&info["net"]~doc)letrestart_on_fail=letdoc="Restart on fail"inArg.(value&flag&info["restart-on-fail"]~doc)letexit_code=letdoc="Exit code to restart on"inArg.(value&opt_allint[]&info["exit-code"]~doc)lettimestamp_c=letparses=matchPtime.of_rfc3339swith|Ok(t,_,_)->`Okt|Error_->(* let's try to add T00:00:00-00:00 *)matchPtime.of_rfc3339(s^"T00:00:00-00:00")with|Ok(t,_,_)->`Okt|Error_->`Error"couldn't parse timestamp"in(parse,Ptime.pp_rfc3339())letsince=letdoc="Receive data since a specified timestamp (RFC 3339 encoded)"inArg.(value&opt(sometimestamp_c)None&info["since"]~doc)letcount=letdoc="Receive N data records"inArg.(value&optint20&info["count"]~doc)letsince_countsincecount=matchsincewith|None->`Countcount|Somesince->`Sincesinceletversion=Fmt.strf"version v1.2.0 protocol version %a"Vmm_commands.pp_versionVmm_commands.currentletdefault_tmpdir=matchLazy.forceVmm_unix.unamewith|FreeBSD->"/var/run/albatross"|Linux->"/run/albatross"lettmpdir=letdoc="Albatross temporary directory"inArg.(value&optdirdefault_tmpdir&info["tmpdir"]~doc)letset_tmpdirpath=matchFpath.of_stringpathwith|Okpath->Vmm_core.set_tmpdirpath|Error`Msgm->invalid_argmletdefault_dbdir=matchLazy.forceVmm_unix.unamewith|Vmm_unix.FreeBSD->"/var/db/albatross"|Linux->"/var/lib/albatross"letdbdir=letdoc="Albatross database directory"inArg.(value&optdirdefault_dbdir&info["dbdir"]~doc)letset_dbdirpath=matchFpath.of_stringpathwith|Okpath->Vmm_unix.set_dbdirpath|Error`Msgm->invalid_argmletenable_stats=letdoc="Connect to albatross-stats to report statistics"inArg.(value&flag&info["enable-stats"]~doc)letretry_connections=letdoc="Number of retries when connecting to other daemons (log, console, stats etc). 0 aborts after one failure, -1 is unlimited retries."inArg.(value&optint2&info["retry-connections"]~doc)letsystemd_socket_activation=matchLazy.forceVmm_unix.unamewith|FreeBSD->Term.constfalse|Linux->letdoc="Pass this flag when systemd socket activation is being used"inArg.(value&flag&info["systemd-socket-activation"]~doc)letexit_status=function|Ok()->OkSuccess|Errore->Oke(* exit status already in use:
- 0 success
- 2 OCaml exception
- 124 "cli error"
- 125 "internal error"
- 126 (bash) command invoked cannot execute
- 127 (bash) command not found
- 255 OCaml abort
*)letlocal_authentication_failed=119letremote_authentication_failed=120letcommunication_failed=121letconnect_failed=122letremote_command_failed=123letexit_status_to_int=function|Success->0|Local_authentication_failed->local_authentication_failed|Remote_authentication_failed->remote_authentication_failed|Communication_failed->communication_failed|Connect_failed->connect_failed|Remote_command_failed->remote_command_failed|Cli_failed->Term.exit_status_cli_error|Internal_error->Term.exit_status_internal_errorletexits=Term.exit_info~doc:"on communication (read or write) failure"communication_failed::Term.exit_info~doc:"on connection failure"connect_failed::Term.exit_info~doc:"on remote command execution failure"remote_command_failed::Term.default_exitsletauth_exits=[Term.exit_info~doc:"on local authentication failure \
(certificate not accepted by remote)"local_authentication_failed;Term.exit_info~doc:"on remote authentication failure \
(couldn't validate trust anchor)"remote_authentication_failed]